library(tidyverse)     # for data cleaning and plotting
library(lubridate)     # for date manipulation
library(openintro)     # for the abbr2state() function
library(palmerpenguins)# for Palmer penguin data
library(maps)          # for map data
library(ggmap)         # for mapping points on maps
library(gplots)        # for col2hex() function
library(RColorBrewer)  # for color palettes
library(sf)            # for working with spatial data
library(leaflet)       # for highly customizable mapping
library(carData)       # for Minneapolis police stops data
library(ggthemes)      # for more themes (including theme_map())
theme_set(theme_minimal())
# Starbucks locations
Starbucks <- read_csv("https://www.macalester.edu/~ajohns24/Data/Starbucks.csv")

starbucks_us_by_state <- Starbucks %>% 
  filter(Country == "US") %>% 
  count(`State/Province`) %>% 
  mutate(state_name = str_to_lower(abbr2state(`State/Province`))) 

# Lisa's favorite St. Paul places - example for you to create your own data
favorite_stp_by_lisa <- tibble(
  place = c("Home", "Macalester College", "Adams Spanish Immersion", 
            "Spirit Gymnastics", "Bama & Bapa", "Now Bikes",
            "Dance Spectrum", "Pizza Luce", "Brunson's"),
  long = c(-93.1405743, -93.1712321, -93.1451796, 
           -93.1650563, -93.1542883, -93.1696608, 
           -93.1393172, -93.1524256, -93.0753863),
  lat = c(44.950576, 44.9378965, 44.9237914,
          44.9654609, 44.9295072, 44.9436813, 
          44.9399922, 44.9468848, 44.9700727)
  )

#COVID-19 data from the New York Times
covid19 <- read_csv("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv")

Put your homework on GitHub!

If you were not able to get set up on GitHub last week, go here and get set up first. Then, do the following (if you get stuck on a step, don’t worry, I will help! You can always get started on the homework and we can figure out the GitHub piece later):

  • Create a repository on GitHub, giving it a nice name so you know it is for the 4th weekly exercise assignment (follow the instructions in the document/video).
  • Copy the repo name so you can clone it to your computer. In R Studio, go to file –> New project –> Version control –> Git and follow the instructions from the document/video.
  • Download the code from this document and save it in the repository folder/project on your computer.
  • In R Studio, you should then see the .Rmd file in the upper right corner in the Git tab (along with the .Rproj file and probably .gitignore).
  • Check all the boxes of the files in the Git tab under Stage and choose commit.
  • In the commit window, write a commit message, something like “Initial upload” would be appropriate, and commit the files.
  • Either click the green up arrow in the commit window or close the commit window and click the green up arrow in the Git tab to push your changes to GitHub.
  • Refresh your GitHub page (online) and make sure the new documents have been pushed out.
  • Back in R Studio, knit the .Rmd file. When you do that, you should have two (as long as you didn’t make any changes to the .Rmd file, in which case you might have three) files show up in the Git tab - an .html file and an .md file. The .md file is something we haven’t seen before and is here because I included keep_md: TRUE in the YAML heading. The .md file is a markdown (NOT R Markdown) file that is an interim step to creating the html file. They are displayed fairly nicely in GitHub, so we want to keep it and look at it there. Click the boxes next to these two files, commit changes (remember to include a commit message), and push them (green up arrow).
  • As you work through your homework, save and commit often, push changes occasionally (maybe after you feel finished with an exercise?), and go check to see what the .md file looks like on GitHub.
  • If you have issues, let me know! This is new to many of you and may not be intuitive at first. But, I promise, you’ll get the hang of it!

Instructions

  • Put your name at the top of the document.

  • For ALL graphs, you should include appropriate labels.

  • Feel free to change the default theme, which I currently have set to theme_minimal().

  • Use good coding practice. Read the short sections on good code with pipes and ggplot2. This is part of your grade!

  • When you are finished with ALL the exercises, uncomment the options at the top so your document looks nicer. Don’t do it before then, or else you might miss some important warnings and messages.

Warm-up exercises from tutorial

These exercises will reiterate what you learned in the “Mapping data with R” tutorial. If you haven’t gone through the tutorial yet, you should do that first.

Starbucks locations (ggmap)

  1. Add the Starbucks locations to a world map. Add an aesthetic to the world map that sets the color of the points according to the ownership type. What, if anything, can you deduce from this visualization?
world <- get_stamenmap(
    bbox = c(left = -180, bottom = -57, right = 179, top = 82.1), 
    maptype = "terrain",
    zoom = 2)

Starbucks_Clean <- Starbucks %>% 
  rename(ownership_type = `Ownership Type`)
  
ggmap(world) + 
  geom_point(data = Starbucks_Clean, 
             aes(x = Longitude, 
                 y = Latitude, 
                 color = ownership_type), 
             alpha = .4, 
             size = 1) +
  labs(title = "Worldwide Starbucks Locations and Ownership Type", 
       color = "Ownership Type", 
       x = "",
       y = "")

Observations: It appears that most Starbucks are either company owned or licensed. As far as I can tell, there are very few franchise Starbucks. North America and Europe are particularly dominated by licensed and company owned stores. Interestingly, stores that are joint ventures are mostly found in Eastern Europe, S. Asia, and E. Asia. There are very few Starbucks in S.America and Africa– two areas that produce a lot of coffee beans. It’s difficult to deduce much beyond these spatial distribution patterns.

  1. Construct a new map of Starbucks locations in the Twin Cities metro area (approximately the 5 county metro area).
twin_cities <- get_stamenmap(
    bbox = c(left = -93.5, bottom = 44.8, right = -92.8, top = 45.2), 
    maptype = "terrain",
    zoom = 11)

Starbucks_TC <- Starbucks_Clean %>% 
  filter(Country == "US",
         `State/Province` == "MN")

ggmap(twin_cities) + 
  geom_point(data = Starbucks_TC, 
             aes(x = Longitude, 
                 y = Latitude)) +
  labs(title = "Starbucks in the Twin Cities")

  1. In the Twin Cities plot, play with the zoom number. What does it do? (just describe what it does - don’t actually include more than one map).

Discussion: The zoom number changes the level of detail in the map. The higher the number, the more zoomed in the map is, and the more detail it provides. As the number decreases, the map zooms out, showing a greater area but with less detail.

  1. Try a couple different map types (see get_stamenmap() in help and look at maptype). Include a map with one of the other map types.
twin_cities2 <- get_stamenmap(
    bbox = c(left = -93.5, bottom = 44.8, right = -92.8, top = 45.2), 
    maptype = "toner-2010",
    zoom = 11)

ggmap(twin_cities2) + 
  geom_point(data = Starbucks_TC, 
             aes(x = Longitude, 
                 y = Latitude), 
             color = "red") +
  labs(title = "Starbucks in the Twin Cities", 
       x = "", 
       y = "")

  1. Add a point to the map that indicates Macalester College and label it appropriately. There are many ways you can do think, but I think it’s easiest with the annotate() function (see ggplot2 cheatsheet).
ggmap(twin_cities2) + 
  geom_point(data = Starbucks_TC, 
             aes(x = Longitude, 
                 y = Latitude), 
             size = .5, 
             color = "dark green") +
  annotate("point",
           x = -93.1691, 
           y = 44.9379, 
           label = "Macalester College", 
           color = "orange", 
           size = 3) +
   annotate("text",
           x = -93.1691, 
           y = 44.92, 
           label = "Macalester College", 
           color = "orange", 
           size = 3) +
  labs(title = "Starbucks in TC + Macalester College", 
       x = "", 
       y = "")

Choropleth maps with Starbucks data (geom_map())

The example I showed in the tutorial did not account for population of each state in the map. In the code below, a new variable is created, starbucks_per_10000, that gives the number of Starbucks per 10,000 people. It is in the starbucks_with_2018_pop_est dataset.

census_pop_est_2018 <- read_csv("https://www.dropbox.com/s/6txwv3b4ng7pepe/us_census_2018_state_pop_est.csv?dl=1") %>% 
  separate(state, into = c("dot","state"), extra = "merge") %>% 
  select(-dot) %>% 
  mutate(state = str_to_lower(state))

starbucks_with_2018_pop_est <-
  starbucks_us_by_state %>% 
  left_join(census_pop_est_2018,
            by = c("state_name" = "state")) %>% 
  mutate(starbucks_per_10000 = (n/est_pop_2018)*10000)
  1. dplyr review: Look through the code above and describe what each line of code does.

Answer: The first line of code reads in the csv file and creates a data frame with its information called ‘census_pop_est_2018.’ Following that line of code, the next one use the separate function to turn the ‘state’ column into two separate columns that separate the state name from the period in front of it. Next, the select function is used to select all variables in the data frame except the period, which in this case means its just the state name. Then, you use the mutate function to essentially change the state variable using the str_to_lower function to make all the states’ names be in lower case.

The code of chunk that follows creates a new data frame called ‘starbucks_with_2018_pop_est’ by piping into the starbucks_us_by_state data frame. From it, this new data frame uses the left_join function to append the starbucks_us_by_state data frame with the variables from the census_pop_est_2018 data frame. These data sets are joined by identifying the unique id that is shared between the two of the data sets, in this case ‘state name’ and ‘state’, and appending the information based on this shared variable. The new data set is then given another variable with the mutate function called ‘starbucks_per_10000.’ This new variable calculates the number of Starbucks per 10000 people by dividing the number of stores in each state by the population of each state, and then multiplying that by 10000.

  1. Create a choropleth map that shows the number of Starbucks per 10,000 people on a map of the US. Use a new fill color, add points for all Starbucks in the US (except Hawaii and Alaska), add an informative title for the plot, and include a caption that says who created the plot (you!). Make a conclusion about what you observe.
US_Map <- map_data("state")

Starbucks_US <- Starbucks %>% 
  filter(Country == "US")

Starbucks_US_Points <- starbucks_with_2018_pop_est %>% 
  left_join(Starbucks_US, 
            by = c("State/Province")) %>% 
  filter(!state_name %in% c("alaska", "hawaii"))

Starbucks_US_Points %>% 
  rename(region = state_name) %>% 
  ggplot() +
  geom_map(map = US_Map, 
           aes(map_id = region, 
               fill = starbucks_per_10000)) + 
  scale_fill_gradient(low = "#27251F", high = "#00704A") +
  expand_limits(x = US_Map$long, y = US_Map$lat) + 
  theme_map() +
  geom_point(data = Starbucks_US_Points, 
             aes(x = Longitude, 
                 y = Latitude), 
             color = "grey",
             size = 1, 
             alpha = .5) +
  theme(legend.background = element_blank(), 
        legend.position = 'right') +
  labs(title = "Starbucks per 10,000 People", 
       caption = "Gabriel Reynolds", 
       fill = "Starbucks per 10,000 People")

Discussion: Interestingly, there is a higher number of Starbucks per 10,000 people in the western part of the US than anywhere else. Visually, it appears that there are many more stores in the East, but considering the population distribution, apparently there are more Starbucks per people in the West, which I suppose makes sense because of Starbucks’ founding in Seattle. Colorado also stands out as a state with a very high number of stores per 10,000 people, which I wasn’t expecting.

A few of your favorite things (leaflet)

  1. In this exercise, you are going to create a single map of some of your favorite places! The end result will be one map that satisfies the criteria below.
  • Create a data set using the tibble() function that has 10-15 rows of your favorite places. The columns will be the name of the location, the latitude, the longitude, and a column that indicates if it is in your top 3 favorite locations or not. For an example of how to use tibble(), look at the favorite_stp_by_lisa I created in the data R code chunk at the beginning.

  • Create a leaflet map that uses circles to indicate your favorite places. Label them with the name of the place. Choose the base map you like best. Color your 3 favorite places differently than the ones that are not in your top 3 (HINT: colorFactor()). Add a legend that explains what the colors mean.

  • Connect all your locations together with a line in a meaningful way (you may need to order them differently in the original data).

  • If there are other variables you want to add that could enhance your plot, do that now.

favorite_places <- tibble(
  name = c("minnehaha_falls", "saint_anthony_falls", "como_park", "art_institute", "grand_marais", "door_county", "duluth", "aster_cafe", "bell_museum", "pike_island"), 
  long = c(-93.209, -93.255848, -93.154268, -93.274030, -90.337863, -87.194083, -92.097201, -93.255249, -93.187747, -93.166682), 
  lat = c(44.915001, 44.982000, 44.982330, 44.958326, 47.751534, 44.985930, 46.788668, 44.984456, 44.991583, 44.891313),
  top_3 = c(TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)
)

pal <- colorFactor(palette=c("blue", "orange"), 
                     domain = favorite_places$top_3)

leaflet(favorite_places) %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  addCircles(label = ~name,
             color = ~pal(top_3)) %>% 
  addLegend("topleft", pal = pal, values = ~top_3, bins=1, title = "Top 3 Favorite Places") %>%
  addPolylines(lng = c(-93.209, -93.274030, -93.255848, -93.255249, -92.097201, -90.337863, -87.194083, -93.166682, -93.154268, -93.187747), 
               lat = c(44.915001,44.958326, 44.982000, 44.984456, 46.788668, 47.751534, 44.985930, 44.891313, 44.982330, 44.99158),
               color="purple", weight = 3)

Revisiting old datasets

This section will revisit some datasets we have used previously and bring in a mapping component.

Bicycle-Use Patterns

The data come from Washington, DC and cover the last quarter of 2014.

Two data tables are available:

  • Trips contains records of individual rentals
  • Stations gives the locations of the bike rental stations

Here is the code to read in the data. We do this a little differently than usualy, which is why it is included here rather than at the top of this file. To avoid repeatedly re-reading the files, start the data import chunk with {r cache = TRUE} rather than the usual {r}. This code reads in the large dataset right away.

data_site <- 
  "https://www.macalester.edu/~dshuman1/data/112/2014-Q4-Trips-History-Data.rds" 
Trips <- readRDS(gzcon(url(data_site)))
Stations<-read_csv("http://www.macalester.edu/~dshuman1/data/112/DC-Stations.csv")
  1. Use the latitude and longitude variables in Stations to make a visualization of the total number of departures from each station in the Trips data. Use either color or size to show the variation in number of departures. This time, plot the points on top of a map. Use any of the mapping tools you’d like.
Trips2 <- Trips %>% 
  group_by(sstation) %>% 
  summarize(n_departures = n()) %>% 
  arrange(desc(n_departures)) %>% 
  left_join(Stations, 
            by = c("sstation"="name")) 


pal2 <- colorBin(palette="YlGnBu",
                 domain = Trips2$n_departures,
                 bins=6)

leaflet(Trips2) %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  addCircles(label = ~sstation, 
             color = ~pal2(n_departures), 
             opacity = 1) %>% 
  addLegend("topleft", values = ~n_departures, pal=pal2, bins=6, title="Number of Station Departures")
  1. Only 14.4% of the trips in our data are carried out by casual users. Create a plot that shows which area(s) have stations with a much higher percentage of departures by casual users. What patterns do you notice? Also plot this on top of a map. I think it will be more clear what the patterns are.
CasualTrips <- Trips %>% 
  group_by(sstation) %>%
  mutate(casual = client == "Casual", 
         registered = client == "Registered") %>% 
  group_by(sstation) %>% 
  summarize(prop_casual = sum(casual)/(sum(registered) + sum(casual))) %>% 
  arrange(desc(prop_casual)) %>% 
  left_join(Stations, 
            by = c("sstation"="name")) 

pal3 <- colorBin(palette = "YlGnBu", 
                 domain = CasualTrips$prop_casual)

leaflet(CasualTrips) %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  addCircles(label = ~sstation, 
             color = ~pal3(prop_casual), 
             opacity = 1) %>% 
  addLegend("bottomleft", pal = pal3, values = ~prop_casual, bins = 6, title = "Proportion of Casual Clients")

Observations: With the map, it’s clearer to identify which stations have higher proportions of causal clients and why. It appears that the stations with the highest proportion of casual clients are around key tourist attractions in D.C. For instance, there are stations around the main Mall, the Washington Monument, the MLK Memorial, the Lincoln Memorial, and the Capitol Building all with notably higher proportions of casual clients. This makes sense as tourists will be more likely to visit these areas and choose to commute with bikes.

COVID-19 data

The following exercises will use the COVID-19 data from the NYT.

  1. Create a map that colors the states by the most recent cumulative number of COVID-19 cases (remember, these data report cumulative numbers so you don’t need to compute that). Describe what you see. What is the problem with this map?
StatesMap <- map_data("state")

covid19 %>% 
  mutate(region = tolower(state)) %>% 
  group_by(region) %>% 
  mutate(cum_cases = max(cases)) %>% 
  ggplot() +
  geom_map(map = StatesMap,
           aes(map_id = region,
               fill = cum_cases)) +
  expand_limits(x = StatesMap$long, y = StatesMap$lat) + 
  theme_map() + 
  labs(title = "COVID-19 Cases by State", 
       x = "", 
       y = "", 
       fill = "Total Cumulative Cases by State") +
  theme(legend.position = "right")

Discussion: This map shows the number of cumulative COVID-19 cases by state. From the map, we can see that California, Texas, New York, Florida, and Illinois are amonng the states with the most cases in the US. The issue with this map is that it’s looking at raw counts of cases per state, rather than a per capita measure (a rate). Only looking at raw data will make it such that the most populous states will always appear to be the most in magnitude. IT wouldn’t be fair to compare raw counts of covid cases between California and Wyoming, the population sizes are drastically different. It would be more appropriate to look at a standardized measure or rate of COVID cases instead.

  1. Now add the population of each state to the dataset and color the states by most recent cumulative cases/10,000 people. See the code for doing this with the Starbucks data. You will need to make some modifications.
covid19_with_pop_est <- covid19 %>%
  mutate(state = tolower(state)) %>% 
  left_join(census_pop_est_2018, 
            by = c("state")) %>% 
  group_by(state) %>% 
  mutate(cum_cases = max(cases), 
            cases_per_10000 = (cum_cases/est_pop_2018) * 10000)

covid19_with_pop_est %>% 
  ggplot() +
  geom_map(map = StatesMap,
           aes(map_id = state,
               fill = cases_per_10000)) +
  expand_limits(x = StatesMap$long, y = StatesMap$lat) + 
  theme_map() + 
  labs(title = "COVID-19 Cases per 10,000 People by State", 
       x = "", 
       y = "", 
       fill = "Total Cumulative Cases per 10000 People by State") +
  theme(legend.position = "bottom")

  1. CHALLENGE Choose 4 dates spread over the time period of the data and create the same map as in exercise 12 for each of the dates. Display the four graphs together using faceting. What do you notice?
covid_4day_sample <- covid19 %>% 
  mutate(state = tolower(state)) %>% 
  left_join(census_pop_est_2018, 
            by = c("state")) %>% 
  group_by(date, state) %>% 
  mutate(cum_cases = max(cases), 
            cases_per_10000 = (cum_cases/est_pop_2018) * 10000)

covid_4day_sample %>% 
  filter(date == "2021-01-01"|
         date == "2020-10-01"|
         date == "2020-07-01"|
         date == "2020-04-01") %>% 
  ggplot() +
  geom_map(map = StatesMap,
           aes(map_id = state,
               fill = cases_per_10000)) +
  expand_limits(x = StatesMap$long, y = StatesMap$lat) + 
  theme_map() +
  labs(title = "COVID-19 Cases per 10,000 People by State", 
       x = "", 
       y = "", 
       fill = "Total Cumulative Cases per 10000 People by State") +
  theme(legend.position = "bottom")+
  facet_wrap(vars(date))

Discussion: You can see from the maps that Covid cases per 10,000 have drastically increased since the beginning of the pandemic. Because the scale is fixed for each of the maps, its bound to the upper limits of the most recent case counts, which are much much higher than to start. This causes the initial maps in the earlier months of the pandemic to appear as if the cases were very low, though this is only a result of the high cases numbers today. As such, we’re unable to see the gradation in the severity of cases across states until about October, which still doesn’t provide a ton of clarity around which states are doing worse/better than others. You can see throughout these maps, however, that the Pacific Northwest has consistently low cases throughout this time period sample. I’m surprised to see how high the cases per 10,000 has gotten for the Dakotas, it appears they are among the highest case counts in the country.

Minneapolis police stops

These exercises use the datasets MplsStops and MplsDemo from the carData library. Search for them in Help to find out more information.

  1. Use the MplsStops dataset to find out how many stops there were for each neighborhood and the proportion of stops that were for a suspicious vehicle or person. Sort the results from most to least number of stops. Save this as a dataset called mpls_suspicious and display the table.
mpls_suspicious <- MplsStops %>% 
  mutate(sus = problem == "suspicious") %>% 
  group_by(neighborhood) %>% 
  summarize(n_stops = n(), 
            sus_stops = sum(sus), 
            prop_sus = sus_stops/n_stops) %>% 
  arrange(desc(n_stops))

head(mpls_suspicious)
  1. Use a leaflet map and the MplsStops dataset to display each of the stops on a map as a small point. Color the points differently depending on whether they were for suspicious vehicle/person or a traffic stop (the problem variable). HINTS: use addCircleMarkers, set stroke = FAlSE, use colorFactor() to create a palette.
pal4 <- colorFactor(palette = c("blue", "yellow"), 
                    domain = MplsStops$problem)

leaflet(MplsStops) %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  addCircles(lng = ~long,
             lat = ~lat,
             radius = 3,
             color = ~pal4(problem),
             stroke=FALSE, 
             label= ~problem) %>% 
    addLegend("topleft", pal = pal4, values = ~problem, bins=2, title = "Stop Type")
  1. Save the folder from moodle called Minneapolis_Neighborhoods into your project/repository folder for this assignment. Make sure the folder is called Minneapolis_Neighborhoods. Use the code below to read in the data and make sure to delete the eval=FALSE. Although it looks like it only links to the .sph file, you need the entire folder of files to create the mpls_nbhd data set. These data contain information about the geometries of the Minneapolis neighborhoods. Using the mpls_nbhd dataset as the base file, join the mpls_suspicious and MplsDemo datasets to it by neighborhood (careful, they are named different things in the different files). Call this new dataset mpls_all.
mpls_nbhd <- st_read("Minneapolis_Neighborhoods/Minneapolis_Neighborhoods.shp", quiet = TRUE)
mpls_suspicious_full <- mpls_suspicious %>% 
  left_join(MplsStops,
            by="neighborhood")

mpls_nbhd_copy <- mpls_nbhd %>% 
  left_join(mpls_suspicious_full,
            by=c("BDNAME"="neighborhood"))

mpls_all <- mpls_nbhd_copy %>% 
  left_join(MplsDemo, 
            by=c("BDNAME"="neighborhood"))
  1. Use leaflet to create a map from the mpls_all data that colors the neighborhoods by prop_suspicious. Display the neighborhood name as you scroll over it. Describe what you observe in the map.
  pal5 <- colorBin(palette="Greens",
                 domain = mpls_all$prop_sus,
                 bins=6)

leaflet(mpls_all) %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  addCircles(lng = ~long,
             lat = ~lat,
             radius = 3,
             stroke=FALSE, 
             label = ~BDNAME,
             color = ~pal5(prop_sus),
             opacity=1) %>% 
  addLegend("topleft", values = ~prop_sus, pal=pal5, bins=6, title="Proportion of Suspicious Police Stops")

Observations: The Neighborhoods that appear to have the highest proportion of police stops for people deemed suspicious are clustered around the downtown area and the neighborhoods in south Minneapolis that are along 35W such as East Phillips, Central, Powderhorn Park, Seward, Longfelllow, and Corcoran. There are few stops for ‘suspicious’ people in Northeast Minneapolis and around other parts near the U of M campus. Same with chain of lakes area. From personally living in the Twin Cities all my life, I know these trends to roughly equate to the affluence of these neighborhoods. The neighborhoods with wealthier people tend to have lower proportions of stops for people who are ‘suspicious.’

  1. Use leaflet to create a map of your own choosing. Come up with a question you want to try to answer and use the map to help answer that question. Describe what your map shows.

Question: Are police stops in certain neighborhoods more likely to result in a vehicle search than other neighborhoods?

VehicleSearched <- MplsStops %>%
  filter(!is.na(vehicleSearch)) %>% 
  mutate(search_yes = vehicleSearch == "YES") %>% 
  group_by(neighborhood) %>% 
  summarize(n_stops = n(), 
            v_search_yes = sum(search_yes == TRUE), 
            prop_search_yes = v_search_yes/n_stops) %>% 
  arrange(desc(prop_search_yes))

VehicleSearched_full <- VehicleSearched %>% 
  left_join(MplsStops,
            by="neighborhood")

VehicleSearched_nbhd <- mpls_nbhd %>% 
  left_join(VehicleSearched_full,
            by=c("BDNAME"="neighborhood"))

VehicleSearchedAll <- VehicleSearched_nbhd %>% 
  left_join(MplsDemo, 
            by=c("BDNAME"="neighborhood"))

pal6 <- colorBin(palette = "Greens", 
                    domain = VehicleSearchedAll$prop_search_yes, 
                    bins = 6)

leaflet(VehicleSearchedAll) %>% 
  addProviderTiles(providers$Stamen.Toner) %>% 
  addCircles(lng = ~long,
             lat = ~lat,
             radius = 3,
             stroke=FALSE, 
             label = ~BDNAME,
             color = ~pal6(prop_search_yes),
             opacity=1) %>% 
  addLegend("topright", values = ~prop_search_yes, pal=pal6, bins=1, title="Proportion of Stops that Led to a Vehicle Search")

Discussion: My map illustrates the proportion of stops by the police that result in a vehicle search, categorized by neighborhood in Minneapolis. Areas on the map that are darker indicate a higher proportion of total stops that result in a vehicle search. From the map, we can see a clear spatial pattern. Vehicle searches happen at a higher rate in North Minneapolis and the Phillips Neighborhood than the rest of Minneapolis. Without more context and data analysis, it might be erroneous of me to make any causal inferences. I do suspect, however, that this is indicative of a racial imbalance in the proportion of vehicle searches that occur as North Minneapolis and the Phillips neighborhood have a higher percentage of POC living in these areas. We can definitely see that there is disparity in terms of the rate at which vehicle searches occur across neighborhoods. I would be curious to see how different the map might be and how different the rates might be if I had calculated for any search, whether of a person or of a vehicle.

LS0tCnRpdGxlOiAnV2Vla2x5IEV4ZXJjaXNlcyAjNCcKYXV0aG9yOiAiR2FicmllbCBSZXlub2xkcyIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAga2VlcF9tZDogVFJVRQogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlIAogICAgdGhlbWU6IHlldGkKLS0tCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICMgZm9yIGRhdGEgY2xlYW5pbmcgYW5kIHBsb3R0aW5nCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24KbGlicmFyeShvcGVuaW50cm8pICAgICAjIGZvciB0aGUgYWJicjJzdGF0ZSgpIGZ1bmN0aW9uCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpIyBmb3IgUGFsbWVyIHBlbmd1aW4gZGF0YQpsaWJyYXJ5KG1hcHMpICAgICAgICAgICMgZm9yIG1hcCBkYXRhCmxpYnJhcnkoZ2dtYXApICAgICAgICAgIyBmb3IgbWFwcGluZyBwb2ludHMgb24gbWFwcwpsaWJyYXJ5KGdwbG90cykgICAgICAgICMgZm9yIGNvbDJoZXgoKSBmdW5jdGlvbgpsaWJyYXJ5KFJDb2xvckJyZXdlcikgICMgZm9yIGNvbG9yIHBhbGV0dGVzCmxpYnJhcnkoc2YpICAgICAgICAgICAgIyBmb3Igd29ya2luZyB3aXRoIHNwYXRpYWwgZGF0YQpsaWJyYXJ5KGxlYWZsZXQpICAgICAgICMgZm9yIGhpZ2hseSBjdXN0b21pemFibGUgbWFwcGluZwpsaWJyYXJ5KGNhckRhdGEpICAgICAgICMgZm9yIE1pbm5lYXBvbGlzIHBvbGljZSBzdG9wcyBkYXRhCmxpYnJhcnkoZ2d0aGVtZXMpICAgICAgIyBmb3IgbW9yZSB0aGVtZXMgKGluY2x1ZGluZyB0aGVtZV9tYXAoKSkKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKYGBgCgpgYGB7ciBkYXRhfQojIFN0YXJidWNrcyBsb2NhdGlvbnMKU3RhcmJ1Y2tzIDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5tYWNhbGVzdGVyLmVkdS9+YWpvaG5zMjQvRGF0YS9TdGFyYnVja3MuY3N2IikKCnN0YXJidWNrc191c19ieV9zdGF0ZSA8LSBTdGFyYnVja3MgJT4lIAogIGZpbHRlcihDb3VudHJ5ID09ICJVUyIpICU+JSAKICBjb3VudChgU3RhdGUvUHJvdmluY2VgKSAlPiUgCiAgbXV0YXRlKHN0YXRlX25hbWUgPSBzdHJfdG9fbG93ZXIoYWJicjJzdGF0ZShgU3RhdGUvUHJvdmluY2VgKSkpIAoKIyBMaXNhJ3MgZmF2b3JpdGUgU3QuIFBhdWwgcGxhY2VzIC0gZXhhbXBsZSBmb3IgeW91IHRvIGNyZWF0ZSB5b3VyIG93biBkYXRhCmZhdm9yaXRlX3N0cF9ieV9saXNhIDwtIHRpYmJsZSgKICBwbGFjZSA9IGMoIkhvbWUiLCAiTWFjYWxlc3RlciBDb2xsZWdlIiwgIkFkYW1zIFNwYW5pc2ggSW1tZXJzaW9uIiwgCiAgICAgICAgICAgICJTcGlyaXQgR3ltbmFzdGljcyIsICJCYW1hICYgQmFwYSIsICJOb3cgQmlrZXMiLAogICAgICAgICAgICAiRGFuY2UgU3BlY3RydW0iLCAiUGl6emEgTHVjZSIsICJCcnVuc29uJ3MiKSwKICBsb25nID0gYygtOTMuMTQwNTc0MywgLTkzLjE3MTIzMjEsIC05My4xNDUxNzk2LCAKICAgICAgICAgICAtOTMuMTY1MDU2MywgLTkzLjE1NDI4ODMsIC05My4xNjk2NjA4LCAKICAgICAgICAgICAtOTMuMTM5MzE3MiwgLTkzLjE1MjQyNTYsIC05My4wNzUzODYzKSwKICBsYXQgPSBjKDQ0Ljk1MDU3NiwgNDQuOTM3ODk2NSwgNDQuOTIzNzkxNCwKICAgICAgICAgIDQ0Ljk2NTQ2MDksIDQ0LjkyOTUwNzIsIDQ0Ljk0MzY4MTMsIAogICAgICAgICAgNDQuOTM5OTkyMiwgNDQuOTQ2ODg0OCwgNDQuOTcwMDcyNykKICApCgojQ09WSUQtMTkgZGF0YSBmcm9tIHRoZSBOZXcgWW9yayBUaW1lcwpjb3ZpZDE5IDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnl0aW1lcy9jb3ZpZC0xOS1kYXRhL21hc3Rlci91cy1zdGF0ZXMuY3N2IikKCmBgYAoKIyMgUHV0IHlvdXIgaG9tZXdvcmsgb24gR2l0SHViIQoKSWYgeW91IHdlcmUgbm90IGFibGUgdG8gZ2V0IHNldCB1cCBvbiBHaXRIdWIgbGFzdCB3ZWVrLCBnbyBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2xsZW5kd2F5L2dpdGh1Yl9mb3JfY29sbGFib3JhdGlvbi9ibG9iL21hc3Rlci9naXRodWJfZm9yX2NvbGxhYm9yYXRpb24ubWQpIGFuZCBnZXQgc2V0IHVwIGZpcnN0LiBUaGVuLCBkbyB0aGUgZm9sbG93aW5nIChpZiB5b3UgZ2V0IHN0dWNrIG9uIGEgc3RlcCwgZG9uJ3Qgd29ycnksIEkgd2lsbCBoZWxwISBZb3UgY2FuIGFsd2F5cyBnZXQgc3RhcnRlZCBvbiB0aGUgaG9tZXdvcmsgYW5kIHdlIGNhbiBmaWd1cmUgb3V0IHRoZSBHaXRIdWIgcGllY2UgbGF0ZXIpOgoKKiBDcmVhdGUgYSByZXBvc2l0b3J5IG9uIEdpdEh1YiwgZ2l2aW5nIGl0IGEgbmljZSBuYW1lIHNvIHlvdSBrbm93IGl0IGlzIGZvciB0aGUgNHRoIHdlZWtseSBleGVyY2lzZSBhc3NpZ25tZW50IChmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBpbiB0aGUgZG9jdW1lbnQvdmlkZW8pLiAgCiogQ29weSB0aGUgcmVwbyBuYW1lIHNvIHlvdSBjYW4gY2xvbmUgaXQgdG8geW91ciBjb21wdXRlci4gSW4gUiBTdHVkaW8sIGdvIHRvIGZpbGUgLS0+IE5ldyBwcm9qZWN0IC0tPiBWZXJzaW9uIGNvbnRyb2wgLS0+IEdpdCBhbmQgZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMgZnJvbSB0aGUgZG9jdW1lbnQvdmlkZW8uICAKKiBEb3dubG9hZCB0aGUgY29kZSBmcm9tIHRoaXMgZG9jdW1lbnQgYW5kIHNhdmUgaXQgaW4gdGhlIHJlcG9zaXRvcnkgZm9sZGVyL3Byb2plY3Qgb24geW91ciBjb21wdXRlci4gIAoqIEluIFIgU3R1ZGlvLCB5b3Ugc2hvdWxkIHRoZW4gc2VlIHRoZSAuUm1kIGZpbGUgaW4gdGhlIHVwcGVyIHJpZ2h0IGNvcm5lciBpbiB0aGUgR2l0IHRhYiAoYWxvbmcgd2l0aCB0aGUgLlJwcm9qIGZpbGUgYW5kIHByb2JhYmx5IC5naXRpZ25vcmUpLiAgCiogQ2hlY2sgYWxsIHRoZSBib3hlcyBvZiB0aGUgZmlsZXMgaW4gdGhlIEdpdCB0YWIgdW5kZXIgU3RhZ2UgYW5kIGNob29zZSBjb21taXQuICAKKiBJbiB0aGUgY29tbWl0IHdpbmRvdywgd3JpdGUgYSBjb21taXQgbWVzc2FnZSwgc29tZXRoaW5nIGxpa2UgIkluaXRpYWwgdXBsb2FkIiB3b3VsZCBiZSBhcHByb3ByaWF0ZSwgYW5kIGNvbW1pdCB0aGUgZmlsZXMuICAKKiBFaXRoZXIgY2xpY2sgdGhlIGdyZWVuIHVwIGFycm93IGluIHRoZSBjb21taXQgd2luZG93IG9yIGNsb3NlIHRoZSBjb21taXQgd2luZG93IGFuZCBjbGljayB0aGUgZ3JlZW4gdXAgYXJyb3cgaW4gdGhlIEdpdCB0YWIgdG8gcHVzaCB5b3VyIGNoYW5nZXMgdG8gR2l0SHViLiAgCiogUmVmcmVzaCB5b3VyIEdpdEh1YiBwYWdlIChvbmxpbmUpIGFuZCBtYWtlIHN1cmUgdGhlIG5ldyBkb2N1bWVudHMgaGF2ZSBiZWVuIHB1c2hlZCBvdXQuICAKKiBCYWNrIGluIFIgU3R1ZGlvLCBrbml0IHRoZSAuUm1kIGZpbGUuIFdoZW4geW91IGRvIHRoYXQsIHlvdSBzaG91bGQgaGF2ZSB0d28gKGFzIGxvbmcgYXMgeW91IGRpZG4ndCBtYWtlIGFueSBjaGFuZ2VzIHRvIHRoZSAuUm1kIGZpbGUsIGluIHdoaWNoIGNhc2UgeW91IG1pZ2h0IGhhdmUgdGhyZWUpIGZpbGVzIHNob3cgdXAgaW4gdGhlIEdpdCB0YWIgLSBhbiAuaHRtbCBmaWxlIGFuZCBhbiAubWQgZmlsZS4gVGhlIC5tZCBmaWxlIGlzIHNvbWV0aGluZyB3ZSBoYXZlbid0IHNlZW4gYmVmb3JlIGFuZCBpcyBoZXJlIGJlY2F1c2UgSSBpbmNsdWRlZCBga2VlcF9tZDogVFJVRWAgaW4gdGhlIFlBTUwgaGVhZGluZy4gVGhlIC5tZCBmaWxlIGlzIGEgbWFya2Rvd24gKE5PVCBSIE1hcmtkb3duKSBmaWxlIHRoYXQgaXMgYW4gaW50ZXJpbSBzdGVwIHRvIGNyZWF0aW5nIHRoZSBodG1sIGZpbGUuIFRoZXkgYXJlIGRpc3BsYXllZCBmYWlybHkgbmljZWx5IGluIEdpdEh1Yiwgc28gd2Ugd2FudCB0byBrZWVwIGl0IGFuZCBsb29rIGF0IGl0IHRoZXJlLiBDbGljayB0aGUgYm94ZXMgbmV4dCB0byB0aGVzZSB0d28gZmlsZXMsIGNvbW1pdCBjaGFuZ2VzIChyZW1lbWJlciB0byBpbmNsdWRlIGEgY29tbWl0IG1lc3NhZ2UpLCBhbmQgcHVzaCB0aGVtIChncmVlbiB1cCBhcnJvdykuICAKKiBBcyB5b3Ugd29yayB0aHJvdWdoIHlvdXIgaG9tZXdvcmssIHNhdmUgYW5kIGNvbW1pdCBvZnRlbiwgcHVzaCBjaGFuZ2VzIG9jY2FzaW9uYWxseSAobWF5YmUgYWZ0ZXIgeW91IGZlZWwgZmluaXNoZWQgd2l0aCBhbiBleGVyY2lzZT8pLCBhbmQgZ28gY2hlY2sgdG8gc2VlIHdoYXQgdGhlIC5tZCBmaWxlIGxvb2tzIGxpa2Ugb24gR2l0SHViLiAgCiogSWYgeW91IGhhdmUgaXNzdWVzLCBsZXQgbWUga25vdyEgVGhpcyBpcyBuZXcgdG8gbWFueSBvZiB5b3UgYW5kIG1heSBub3QgYmUgaW50dWl0aXZlIGF0IGZpcnN0LiBCdXQsIEkgcHJvbWlzZSwgeW91J2xsIGdldCB0aGUgaGFuZyBvZiBpdCEgCgoKIyMgSW5zdHJ1Y3Rpb25zCgoqIFB1dCB5b3VyIG5hbWUgYXQgdGhlIHRvcCBvZiB0aGUgZG9jdW1lbnQuIAoKKiAqKkZvciBBTEwgZ3JhcGhzLCB5b3Ugc2hvdWxkIGluY2x1ZGUgYXBwcm9wcmlhdGUgbGFiZWxzLioqIAoKKiBGZWVsIGZyZWUgdG8gY2hhbmdlIHRoZSBkZWZhdWx0IHRoZW1lLCB3aGljaCBJIGN1cnJlbnRseSBoYXZlIHNldCB0byBgdGhlbWVfbWluaW1hbCgpYC4gCgoqIFVzZSBnb29kIGNvZGluZyBwcmFjdGljZS4gUmVhZCB0aGUgc2hvcnQgc2VjdGlvbnMgb24gZ29vZCBjb2RlIHdpdGggW3BpcGVzXShodHRwczovL3N0eWxlLnRpZHl2ZXJzZS5vcmcvcGlwZXMuaHRtbCkgYW5kIFtnZ3Bsb3QyXShodHRwczovL3N0eWxlLnRpZHl2ZXJzZS5vcmcvZ2dwbG90Mi5odG1sKS4gKipUaGlzIGlzIHBhcnQgb2YgeW91ciBncmFkZSEqKgoKKiBXaGVuIHlvdSBhcmUgZmluaXNoZWQgd2l0aCBBTEwgdGhlIGV4ZXJjaXNlcywgdW5jb21tZW50IHRoZSBvcHRpb25zIGF0IHRoZSB0b3Agc28geW91ciBkb2N1bWVudCBsb29rcyBuaWNlci4gRG9uJ3QgZG8gaXQgYmVmb3JlIHRoZW4sIG9yIGVsc2UgeW91IG1pZ2h0IG1pc3Mgc29tZSBpbXBvcnRhbnQgd2FybmluZ3MgYW5kIG1lc3NhZ2VzLgoKCiMjIFdhcm0tdXAgZXhlcmNpc2VzIGZyb20gdHV0b3JpYWwKClRoZXNlIGV4ZXJjaXNlcyB3aWxsIHJlaXRlcmF0ZSB3aGF0IHlvdSBsZWFybmVkIGluIHRoZSAiTWFwcGluZyBkYXRhIHdpdGggUiIgdHV0b3JpYWwuIElmIHlvdSBoYXZlbid0IGdvbmUgdGhyb3VnaCB0aGUgdHV0b3JpYWwgeWV0LCB5b3Ugc2hvdWxkIGRvIHRoYXQgZmlyc3QuCgojIyMgU3RhcmJ1Y2tzIGxvY2F0aW9ucyAoYGdnbWFwYCkKCiAgMS4gQWRkIHRoZSBgU3RhcmJ1Y2tzYCBsb2NhdGlvbnMgdG8gYSB3b3JsZCBtYXAuIEFkZCBhbiBhZXN0aGV0aWMgdG8gdGhlIHdvcmxkIG1hcCB0aGF0IHNldHMgdGhlIGNvbG9yIG9mIHRoZSBwb2ludHMgYWNjb3JkaW5nIHRvIHRoZSBvd25lcnNoaXAgdHlwZS4gV2hhdCwgaWYgYW55dGhpbmcsIGNhbiB5b3UgZGVkdWNlIGZyb20gdGhpcyB2aXN1YWxpemF0aW9uPyAgCiAgCmBgYHtyfQp3b3JsZCA8LSBnZXRfc3RhbWVubWFwKAogICAgYmJveCA9IGMobGVmdCA9IC0xODAsIGJvdHRvbSA9IC01NywgcmlnaHQgPSAxNzksIHRvcCA9IDgyLjEpLCAKICAgIG1hcHR5cGUgPSAidGVycmFpbiIsCiAgICB6b29tID0gMikKClN0YXJidWNrc19DbGVhbiA8LSBTdGFyYnVja3MgJT4lIAogIHJlbmFtZShvd25lcnNoaXBfdHlwZSA9IGBPd25lcnNoaXAgVHlwZWApCiAgCmdnbWFwKHdvcmxkKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IFN0YXJidWNrc19DbGVhbiwgCiAgICAgICAgICAgICBhZXMoeCA9IExvbmdpdHVkZSwgCiAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlLCAKICAgICAgICAgICAgICAgICBjb2xvciA9IG93bmVyc2hpcF90eXBlKSwgCiAgICAgICAgICAgICBhbHBoYSA9IC40LCAKICAgICAgICAgICAgIHNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJXb3JsZHdpZGUgU3RhcmJ1Y2tzIExvY2F0aW9ucyBhbmQgT3duZXJzaGlwIFR5cGUiLCAKICAgICAgIGNvbG9yID0gIk93bmVyc2hpcCBUeXBlIiwgCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIpCiAgICAgICAgICAgICAKCmBgYAoKKipPYnNlcnZhdGlvbnM6KiogSXQgYXBwZWFycyB0aGF0IG1vc3QgU3RhcmJ1Y2tzIGFyZSBlaXRoZXIgY29tcGFueSBvd25lZCBvciBsaWNlbnNlZC4gQXMgZmFyIGFzIEkgY2FuIHRlbGwsIHRoZXJlIGFyZSB2ZXJ5IGZldyBmcmFuY2hpc2UgU3RhcmJ1Y2tzLiBOb3J0aCBBbWVyaWNhIGFuZCBFdXJvcGUgYXJlIHBhcnRpY3VsYXJseSBkb21pbmF0ZWQgYnkgbGljZW5zZWQgYW5kIGNvbXBhbnkgb3duZWQgc3RvcmVzLiBJbnRlcmVzdGluZ2x5LCBzdG9yZXMgdGhhdCBhcmUgam9pbnQgdmVudHVyZXMgYXJlIG1vc3RseSBmb3VuZCBpbiBFYXN0ZXJuIEV1cm9wZSwgUy4gQXNpYSwgYW5kIEUuIEFzaWEuIFRoZXJlIGFyZSB2ZXJ5IGZldyBTdGFyYnVja3MgaW4gUy5BbWVyaWNhIGFuZCBBZnJpY2EtLSB0d28gYXJlYXMgdGhhdCBwcm9kdWNlIGEgbG90IG9mIGNvZmZlZSBiZWFucy4gSXQncyBkaWZmaWN1bHQgdG8gZGVkdWNlIG11Y2ggYmV5b25kIHRoZXNlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIHBhdHRlcm5zLiAKCiAgMi4gQ29uc3RydWN0IGEgbmV3IG1hcCBvZiBTdGFyYnVja3MgbG9jYXRpb25zIGluIHRoZSBUd2luIENpdGllcyBtZXRybyBhcmVhIChhcHByb3hpbWF0ZWx5IHRoZSA1IGNvdW50eSBtZXRybyBhcmVhKS4gIAogIApgYGB7cn0KdHdpbl9jaXRpZXMgPC0gZ2V0X3N0YW1lbm1hcCgKICAgIGJib3ggPSBjKGxlZnQgPSAtOTMuNSwgYm90dG9tID0gNDQuOCwgcmlnaHQgPSAtOTIuOCwgdG9wID0gNDUuMiksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAxMSkKClN0YXJidWNrc19UQyA8LSBTdGFyYnVja3NfQ2xlYW4gJT4lIAogIGZpbHRlcihDb3VudHJ5ID09ICJVUyIsCiAgICAgICAgIGBTdGF0ZS9Qcm92aW5jZWAgPT0gIk1OIikKCmdnbWFwKHR3aW5fY2l0aWVzKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IFN0YXJidWNrc19UQywgCiAgICAgICAgICAgICBhZXMoeCA9IExvbmdpdHVkZSwgCiAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlKSkgKwogIGxhYnModGl0bGUgPSAiU3RhcmJ1Y2tzIGluIHRoZSBUd2luIENpdGllcyIpCmBgYAoKCiAgMy4gSW4gdGhlIFR3aW4gQ2l0aWVzIHBsb3QsIHBsYXkgd2l0aCB0aGUgem9vbSBudW1iZXIuIFdoYXQgZG9lcyBpdCBkbz8gIChqdXN0IGRlc2NyaWJlIHdoYXQgaXQgZG9lcyAtIGRvbid0IGFjdHVhbGx5IGluY2x1ZGUgbW9yZSB0aGFuIG9uZSBtYXApLiAgCiAgCioqRGlzY3Vzc2lvbjoqKiBUaGUgem9vbSBudW1iZXIgY2hhbmdlcyB0aGUgbGV2ZWwgb2YgZGV0YWlsIGluIHRoZSBtYXAuIFRoZSBoaWdoZXIgdGhlIG51bWJlciwgdGhlIG1vcmUgem9vbWVkIGluIHRoZSBtYXAgaXMsIGFuZCB0aGUgbW9yZSBkZXRhaWwgaXQgcHJvdmlkZXMuIEFzIHRoZSBudW1iZXIgZGVjcmVhc2VzLCB0aGUgbWFwIHpvb21zIG91dCwgc2hvd2luZyBhIGdyZWF0ZXIgYXJlYSBidXQgd2l0aCBsZXNzIGRldGFpbC4gCgogIDQuIFRyeSBhIGNvdXBsZSBkaWZmZXJlbnQgbWFwIHR5cGVzIChzZWUgYGdldF9zdGFtZW5tYXAoKWAgaW4gaGVscCBhbmQgbG9vayBhdCBgbWFwdHlwZWApLiBJbmNsdWRlIGEgbWFwIHdpdGggb25lIG9mIHRoZSBvdGhlciBtYXAgdHlwZXMuIAogIApgYGB7cn0KdHdpbl9jaXRpZXMyIDwtIGdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyhsZWZ0ID0gLTkzLjUsIGJvdHRvbSA9IDQ0LjgsIHJpZ2h0ID0gLTkyLjgsIHRvcCA9IDQ1LjIpLCAKICAgIG1hcHR5cGUgPSAidG9uZXItMjAxMCIsCiAgICB6b29tID0gMTEpCgpnZ21hcCh0d2luX2NpdGllczIpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gU3RhcmJ1Y2tzX1RDLCAKICAgICAgICAgICAgIGFlcyh4ID0gTG9uZ2l0dWRlLCAKICAgICAgICAgICAgICAgICB5ID0gTGF0aXR1ZGUpLCAKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlN0YXJidWNrcyBpbiB0aGUgVHdpbiBDaXRpZXMiLCAKICAgICAgIHggPSAiIiwgCiAgICAgICB5ID0gIiIpCmBgYAoKICA1LiBBZGQgYSBwb2ludCB0byB0aGUgbWFwIHRoYXQgaW5kaWNhdGVzIE1hY2FsZXN0ZXIgQ29sbGVnZSBhbmQgbGFiZWwgaXQgYXBwcm9wcmlhdGVseS4gVGhlcmUgYXJlIG1hbnkgd2F5cyB5b3UgY2FuIGRvIHRoaW5rLCBidXQgSSB0aGluayBpdCdzIGVhc2llc3Qgd2l0aCB0aGUgYGFubm90YXRlKClgIGZ1bmN0aW9uIChzZWUgYGdncGxvdDJgIGNoZWF0c2hlZXQpLgogIApgYGB7cn0KZ2dtYXAodHdpbl9jaXRpZXMyKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IFN0YXJidWNrc19UQywgCiAgICAgICAgICAgICBhZXMoeCA9IExvbmdpdHVkZSwgCiAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlKSwgCiAgICAgICAgICAgICBzaXplID0gLjUsIAogICAgICAgICAgICAgY29sb3IgPSAiZGFyayBncmVlbiIpICsKICBhbm5vdGF0ZSgicG9pbnQiLAogICAgICAgICAgIHggPSAtOTMuMTY5MSwgCiAgICAgICAgICAgeSA9IDQ0LjkzNzksIAogICAgICAgICAgIGxhYmVsID0gIk1hY2FsZXN0ZXIgQ29sbGVnZSIsIAogICAgICAgICAgIGNvbG9yID0gIm9yYW5nZSIsIAogICAgICAgICAgIHNpemUgPSAzKSArCiAgIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gLTkzLjE2OTEsIAogICAgICAgICAgIHkgPSA0NC45MiwgCiAgICAgICAgICAgbGFiZWwgPSAiTWFjYWxlc3RlciBDb2xsZWdlIiwgCiAgICAgICAgICAgY29sb3IgPSAib3JhbmdlIiwgCiAgICAgICAgICAgc2l6ZSA9IDMpICsKICBsYWJzKHRpdGxlID0gIlN0YXJidWNrcyBpbiBUQyArIE1hY2FsZXN0ZXIgQ29sbGVnZSIsIAogICAgICAgeCA9ICIiLCAKICAgICAgIHkgPSAiIikKYGBgCgoKIyMjIENob3JvcGxldGggbWFwcyB3aXRoIFN0YXJidWNrcyBkYXRhIChgZ2VvbV9tYXAoKWApCgpUaGUgZXhhbXBsZSBJIHNob3dlZCBpbiB0aGUgdHV0b3JpYWwgZGlkIG5vdCBhY2NvdW50IGZvciBwb3B1bGF0aW9uIG9mIGVhY2ggc3RhdGUgaW4gdGhlIG1hcC4gSW4gdGhlIGNvZGUgYmVsb3csIGEgbmV3IHZhcmlhYmxlIGlzIGNyZWF0ZWQsIGBzdGFyYnVja3NfcGVyXzEwMDAwYCwgdGhhdCBnaXZlcyB0aGUgbnVtYmVyIG9mIFN0YXJidWNrcyBwZXIgMTAsMDAwIHBlb3BsZS4gSXQgaXMgaW4gdGhlIGBzdGFyYnVja3Nfd2l0aF8yMDE4X3BvcF9lc3RgIGRhdGFzZXQuCgpgYGB7cn0KY2Vuc3VzX3BvcF9lc3RfMjAxOCA8LSByZWFkX2NzdigiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy82dHh3djNiNG5nN3BlcGUvdXNfY2Vuc3VzXzIwMThfc3RhdGVfcG9wX2VzdC5jc3Y/ZGw9MSIpICU+JSAKICBzZXBhcmF0ZShzdGF0ZSwgaW50byA9IGMoImRvdCIsInN0YXRlIiksIGV4dHJhID0gIm1lcmdlIikgJT4lIAogIHNlbGVjdCgtZG90KSAlPiUgCiAgbXV0YXRlKHN0YXRlID0gc3RyX3RvX2xvd2VyKHN0YXRlKSkKCnN0YXJidWNrc193aXRoXzIwMThfcG9wX2VzdCA8LQogIHN0YXJidWNrc191c19ieV9zdGF0ZSAlPiUgCiAgbGVmdF9qb2luKGNlbnN1c19wb3BfZXN0XzIwMTgsCiAgICAgICAgICAgIGJ5ID0gYygic3RhdGVfbmFtZSIgPSAic3RhdGUiKSkgJT4lIAogIG11dGF0ZShzdGFyYnVja3NfcGVyXzEwMDAwID0gKG4vZXN0X3BvcF8yMDE4KSoxMDAwMCkKYGBgCgogIDYuICoqYGRwbHlyYCByZXZpZXcqKjogTG9vayB0aHJvdWdoIHRoZSBjb2RlIGFib3ZlIGFuZCBkZXNjcmliZSB3aGF0IGVhY2ggbGluZSBvZiBjb2RlIGRvZXMuCiAgCioqQW5zd2VyOioqIFRoZSBmaXJzdCBsaW5lIG9mIGNvZGUgcmVhZHMgaW4gdGhlIGNzdiBmaWxlIGFuZCBjcmVhdGVzIGEgZGF0YSBmcmFtZSB3aXRoIGl0cyBpbmZvcm1hdGlvbiBjYWxsZWQgJ2NlbnN1c19wb3BfZXN0XzIwMTguJyBGb2xsb3dpbmcgdGhhdCBsaW5lIG9mIGNvZGUsIHRoZSBuZXh0IG9uZSB1c2UgdGhlIHNlcGFyYXRlIGZ1bmN0aW9uIHRvIHR1cm4gdGhlICdzdGF0ZScgY29sdW1uIGludG8gdHdvIHNlcGFyYXRlIGNvbHVtbnMgdGhhdCBzZXBhcmF0ZSB0aGUgc3RhdGUgbmFtZSBmcm9tIHRoZSBwZXJpb2QgaW4gZnJvbnQgb2YgaXQuIE5leHQsIHRoZSBzZWxlY3QgZnVuY3Rpb24gaXMgdXNlZCB0byBzZWxlY3QgYWxsIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBmcmFtZSBleGNlcHQgdGhlIHBlcmlvZCwgd2hpY2ggaW4gdGhpcyBjYXNlIG1lYW5zIGl0cyBqdXN0IHRoZSBzdGF0ZSBuYW1lLiBUaGVuLCB5b3UgdXNlIHRoZSBtdXRhdGUgZnVuY3Rpb24gdG8gZXNzZW50aWFsbHkgY2hhbmdlIHRoZSBzdGF0ZSB2YXJpYWJsZSB1c2luZyB0aGUgc3RyX3RvX2xvd2VyIGZ1bmN0aW9uIHRvIG1ha2UgYWxsIHRoZSBzdGF0ZXMnIG5hbWVzIGJlIGluIGxvd2VyIGNhc2UuIAoKVGhlIGNvZGUgb2YgY2h1bmsgdGhhdCBmb2xsb3dzIGNyZWF0ZXMgYSBuZXcgZGF0YSBmcmFtZSBjYWxsZWQgJ3N0YXJidWNrc193aXRoXzIwMThfcG9wX2VzdCcgYnkgcGlwaW5nIGludG8gdGhlIHN0YXJidWNrc191c19ieV9zdGF0ZSBkYXRhIGZyYW1lLiBGcm9tIGl0LCB0aGlzIG5ldyBkYXRhIGZyYW1lIHVzZXMgdGhlIGxlZnRfam9pbiBmdW5jdGlvbiB0byBhcHBlbmQgdGhlIHN0YXJidWNrc191c19ieV9zdGF0ZSBkYXRhIGZyYW1lIHdpdGggdGhlIHZhcmlhYmxlcyBmcm9tIHRoZSBjZW5zdXNfcG9wX2VzdF8yMDE4IGRhdGEgZnJhbWUuIFRoZXNlIGRhdGEgc2V0cyBhcmUgam9pbmVkIGJ5IGlkZW50aWZ5aW5nIHRoZSB1bmlxdWUgaWQgdGhhdCBpcyBzaGFyZWQgYmV0d2VlbiB0aGUgdHdvIG9mIHRoZSBkYXRhIHNldHMsIGluIHRoaXMgY2FzZSAnc3RhdGUgbmFtZScgYW5kICdzdGF0ZScsIGFuZCBhcHBlbmRpbmcgdGhlIGluZm9ybWF0aW9uIGJhc2VkIG9uIHRoaXMgc2hhcmVkIHZhcmlhYmxlLiBUaGUgbmV3IGRhdGEgc2V0IGlzIHRoZW4gZ2l2ZW4gYW5vdGhlciB2YXJpYWJsZSB3aXRoIHRoZSBtdXRhdGUgZnVuY3Rpb24gY2FsbGVkICdzdGFyYnVja3NfcGVyXzEwMDAwLicgVGhpcyBuZXcgdmFyaWFibGUgY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIFN0YXJidWNrcyBwZXIgMTAwMDAgcGVvcGxlIGJ5IGRpdmlkaW5nIHRoZSBudW1iZXIgb2Ygc3RvcmVzIGluIGVhY2ggc3RhdGUgYnkgdGhlIHBvcHVsYXRpb24gb2YgZWFjaCBzdGF0ZSwgYW5kIHRoZW4gbXVsdGlwbHlpbmcgdGhhdCBieSAxMDAwMC4gIAoKICA3LiBDcmVhdGUgYSBjaG9yb3BsZXRoIG1hcCB0aGF0IHNob3dzIHRoZSBudW1iZXIgb2YgU3RhcmJ1Y2tzIHBlciAxMCwwMDAgcGVvcGxlIG9uIGEgbWFwIG9mIHRoZSBVUy4gVXNlIGEgbmV3IGZpbGwgY29sb3IsIGFkZCBwb2ludHMgZm9yIGFsbCBTdGFyYnVja3MgaW4gdGhlIFVTIChleGNlcHQgSGF3YWlpIGFuZCBBbGFza2EpLCBhZGQgYW4gaW5mb3JtYXRpdmUgdGl0bGUgZm9yIHRoZSBwbG90LCBhbmQgaW5jbHVkZSBhIGNhcHRpb24gdGhhdCBzYXlzIHdobyBjcmVhdGVkIHRoZSBwbG90ICh5b3UhKS4gTWFrZSBhIGNvbmNsdXNpb24gYWJvdXQgd2hhdCB5b3Ugb2JzZXJ2ZS4KCmBgYHtyfQpVU19NYXAgPC0gbWFwX2RhdGEoInN0YXRlIikKClN0YXJidWNrc19VUyA8LSBTdGFyYnVja3MgJT4lIAogIGZpbHRlcihDb3VudHJ5ID09ICJVUyIpCgpTdGFyYnVja3NfVVNfUG9pbnRzIDwtIHN0YXJidWNrc193aXRoXzIwMThfcG9wX2VzdCAlPiUgCiAgbGVmdF9qb2luKFN0YXJidWNrc19VUywgCiAgICAgICAgICAgIGJ5ID0gYygiU3RhdGUvUHJvdmluY2UiKSkgJT4lIAogIGZpbHRlcighc3RhdGVfbmFtZSAlaW4lIGMoImFsYXNrYSIsICJoYXdhaWkiKSkKClN0YXJidWNrc19VU19Qb2ludHMgJT4lIAogIHJlbmFtZShyZWdpb24gPSBzdGF0ZV9uYW1lKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fbWFwKG1hcCA9IFVTX01hcCwgCiAgICAgICAgICAgYWVzKG1hcF9pZCA9IHJlZ2lvbiwgCiAgICAgICAgICAgICAgIGZpbGwgPSBzdGFyYnVja3NfcGVyXzEwMDAwKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjMjcyNTFGIiwgaGlnaCA9ICIjMDA3MDRBIikgKwogIGV4cGFuZF9saW1pdHMoeCA9IFVTX01hcCRsb25nLCB5ID0gVVNfTWFwJGxhdCkgKyAKICB0aGVtZV9tYXAoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gU3RhcmJ1Y2tzX1VTX1BvaW50cywgCiAgICAgICAgICAgICBhZXMoeCA9IExvbmdpdHVkZSwgCiAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJncmV5IiwKICAgICAgICAgICAgIHNpemUgPSAxLCAKICAgICAgICAgICAgIGFscGhhID0gLjUpICsKICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykgKwogIGxhYnModGl0bGUgPSAiU3RhcmJ1Y2tzIHBlciAxMCwwMDAgUGVvcGxlIiwgCiAgICAgICBjYXB0aW9uID0gIkdhYnJpZWwgUmV5bm9sZHMiLCAKICAgICAgIGZpbGwgPSAiU3RhcmJ1Y2tzIHBlciAxMCwwMDAgUGVvcGxlIikKCmBgYAoKKipEaXNjdXNzaW9uOioqIEludGVyZXN0aW5nbHksIHRoZXJlIGlzIGEgaGlnaGVyIG51bWJlciBvZiBTdGFyYnVja3MgcGVyIDEwLDAwMCBwZW9wbGUgaW4gdGhlIHdlc3Rlcm4gcGFydCBvZiB0aGUgVVMgdGhhbiBhbnl3aGVyZSBlbHNlLiBWaXN1YWxseSwgaXQgYXBwZWFycyB0aGF0IHRoZXJlIGFyZSBtYW55IG1vcmUgc3RvcmVzIGluIHRoZSBFYXN0LCBidXQgY29uc2lkZXJpbmcgdGhlIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9uLCBhcHBhcmVudGx5IHRoZXJlIGFyZSBtb3JlIFN0YXJidWNrcyBwZXIgcGVvcGxlIGluIHRoZSBXZXN0LCB3aGljaCBJIHN1cHBvc2UgbWFrZXMgc2Vuc2UgYmVjYXVzZSBvZiBTdGFyYnVja3MnIGZvdW5kaW5nIGluIFNlYXR0bGUuIENvbG9yYWRvIGFsc28gc3RhbmRzIG91dCBhcyBhIHN0YXRlIHdpdGggYSB2ZXJ5IGhpZ2ggbnVtYmVyIG9mIHN0b3JlcyBwZXIgMTAsMDAwIHBlb3BsZSwgd2hpY2ggSSB3YXNuJ3QgZXhwZWN0aW5nLiAKCiMjIyBBIGZldyBvZiB5b3VyIGZhdm9yaXRlIHRoaW5ncyAoYGxlYWZsZXRgKQoKICA4LiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhIHNpbmdsZSBtYXAgb2Ygc29tZSBvZiB5b3VyIGZhdm9yaXRlIHBsYWNlcyEgVGhlIGVuZCByZXN1bHQgd2lsbCBiZSBvbmUgbWFwIHRoYXQgc2F0aXNmaWVzIHRoZSBjcml0ZXJpYSBiZWxvdy4gCgogICogQ3JlYXRlIGEgZGF0YSBzZXQgdXNpbmcgdGhlIGB0aWJibGUoKWAgZnVuY3Rpb24gdGhhdCBoYXMgMTAtMTUgcm93cyBvZiB5b3VyIGZhdm9yaXRlIHBsYWNlcy4gVGhlIGNvbHVtbnMgd2lsbCBiZSB0aGUgbmFtZSBvZiB0aGUgbG9jYXRpb24sIHRoZSBsYXRpdHVkZSwgdGhlIGxvbmdpdHVkZSwgYW5kIGEgY29sdW1uIHRoYXQgaW5kaWNhdGVzIGlmIGl0IGlzIGluIHlvdXIgdG9wIDMgZmF2b3JpdGUgbG9jYXRpb25zIG9yIG5vdC4gRm9yIGFuIGV4YW1wbGUgb2YgaG93IHRvIHVzZSBgdGliYmxlKClgLCBsb29rIGF0IHRoZSBgZmF2b3JpdGVfc3RwX2J5X2xpc2FgIEkgY3JlYXRlZCBpbiB0aGUgZGF0YSBSIGNvZGUgY2h1bmsgYXQgdGhlIGJlZ2lubmluZy4gIAoKICAqIENyZWF0ZSBhIGBsZWFmbGV0YCBtYXAgdGhhdCB1c2VzIGNpcmNsZXMgdG8gaW5kaWNhdGUgeW91ciBmYXZvcml0ZSBwbGFjZXMuIExhYmVsIHRoZW0gd2l0aCB0aGUgbmFtZSBvZiB0aGUgcGxhY2UuIENob29zZSB0aGUgYmFzZSBtYXAgeW91IGxpa2UgYmVzdC4gQ29sb3IgeW91ciAzIGZhdm9yaXRlIHBsYWNlcyBkaWZmZXJlbnRseSB0aGFuIHRoZSBvbmVzIHRoYXQgYXJlIG5vdCBpbiB5b3VyIHRvcCAzIChISU5UOiBgY29sb3JGYWN0b3IoKWApLiBBZGQgYSBsZWdlbmQgdGhhdCBleHBsYWlucyB3aGF0IHRoZSBjb2xvcnMgbWVhbi4gIAogIAogICogQ29ubmVjdCBhbGwgeW91ciBsb2NhdGlvbnMgdG9nZXRoZXIgd2l0aCBhIGxpbmUgaW4gYSBtZWFuaW5nZnVsIHdheSAoeW91IG1heSBuZWVkIHRvIG9yZGVyIHRoZW0gZGlmZmVyZW50bHkgaW4gdGhlIG9yaWdpbmFsIGRhdGEpLiAgCiAgCiAgKiBJZiB0aGVyZSBhcmUgb3RoZXIgdmFyaWFibGVzIHlvdSB3YW50IHRvIGFkZCB0aGF0IGNvdWxkIGVuaGFuY2UgeW91ciBwbG90LCBkbyB0aGF0IG5vdy4gIAogIApgYGB7cn0KZmF2b3JpdGVfcGxhY2VzIDwtIHRpYmJsZSgKICBuYW1lID0gYygibWlubmVoYWhhX2ZhbGxzIiwgInNhaW50X2FudGhvbnlfZmFsbHMiLCAiY29tb19wYXJrIiwgImFydF9pbnN0aXR1dGUiLCAiZ3JhbmRfbWFyYWlzIiwgImRvb3JfY291bnR5IiwgImR1bHV0aCIsICJhc3Rlcl9jYWZlIiwgImJlbGxfbXVzZXVtIiwgInBpa2VfaXNsYW5kIiksIAogIGxvbmcgPSBjKC05My4yMDksIC05My4yNTU4NDgsIC05My4xNTQyNjgsIC05My4yNzQwMzAsIC05MC4zMzc4NjMsIC04Ny4xOTQwODMsIC05Mi4wOTcyMDEsIC05My4yNTUyNDksIC05My4xODc3NDcsIC05My4xNjY2ODIpLCAKICBsYXQgPSBjKDQ0LjkxNTAwMSwgNDQuOTgyMDAwLCA0NC45ODIzMzAsIDQ0Ljk1ODMyNiwgNDcuNzUxNTM0LCA0NC45ODU5MzAsIDQ2Ljc4ODY2OCwgNDQuOTg0NDU2LCA0NC45OTE1ODMsIDQ0Ljg5MTMxMyksCiAgdG9wXzMgPSBjKFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSwgVFJVRSwgRkFMU0UsIEZBTFNFLCBGQUxTRSwgRkFMU0UsIEZBTFNFKQopCgpwYWwgPC0gY29sb3JGYWN0b3IocGFsZXR0ZT1jKCJibHVlIiwgIm9yYW5nZSIpLCAKICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gZmF2b3JpdGVfcGxhY2VzJHRvcF8zKQoKbGVhZmxldChmYXZvcml0ZV9wbGFjZXMpICU+JSAKICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRTdGFtZW4uVG9uZXIpICU+JSAKICBhZGRDaXJjbGVzKGxhYmVsID0gfm5hbWUsCiAgICAgICAgICAgICBjb2xvciA9IH5wYWwodG9wXzMpKSAlPiUgCiAgYWRkTGVnZW5kKCJ0b3BsZWZ0IiwgcGFsID0gcGFsLCB2YWx1ZXMgPSB+dG9wXzMsIGJpbnM9MSwgdGl0bGUgPSAiVG9wIDMgRmF2b3JpdGUgUGxhY2VzIikgJT4lCiAgYWRkUG9seWxpbmVzKGxuZyA9IGMoLTkzLjIwOSwgLTkzLjI3NDAzMCwgLTkzLjI1NTg0OCwgLTkzLjI1NTI0OSwgLTkyLjA5NzIwMSwgLTkwLjMzNzg2MywgLTg3LjE5NDA4MywgLTkzLjE2NjY4MiwgLTkzLjE1NDI2OCwgLTkzLjE4Nzc0NyksIAogICAgICAgICAgICAgICBsYXQgPSBjKDQ0LjkxNTAwMSw0NC45NTgzMjYsIDQ0Ljk4MjAwMCwgNDQuOTg0NDU2LCA0Ni43ODg2NjgsIDQ3Ljc1MTUzNCwgNDQuOTg1OTMwLCA0NC44OTEzMTMsIDQ0Ljk4MjMzMCwgNDQuOTkxNTgpLAogICAgICAgICAgICAgICBjb2xvcj0icHVycGxlIiwgd2VpZ2h0ID0gMykKCmBgYAoKICAKIyMgUmV2aXNpdGluZyBvbGQgZGF0YXNldHMKClRoaXMgc2VjdGlvbiB3aWxsIHJldmlzaXQgc29tZSBkYXRhc2V0cyB3ZSBoYXZlIHVzZWQgcHJldmlvdXNseSBhbmQgYnJpbmcgaW4gYSBtYXBwaW5nIGNvbXBvbmVudC4gCgojIyMgQmljeWNsZS1Vc2UgUGF0dGVybnMKClRoZSBkYXRhIGNvbWUgZnJvbSBXYXNoaW5ndG9uLCBEQyBhbmQgY292ZXIgdGhlIGxhc3QgcXVhcnRlciBvZiAyMDE0LgoKVHdvIGRhdGEgdGFibGVzIGFyZSBhdmFpbGFibGU6CgotIGBUcmlwc2AgY29udGFpbnMgcmVjb3JkcyBvZiBpbmRpdmlkdWFsIHJlbnRhbHMKLSBgU3RhdGlvbnNgIGdpdmVzIHRoZSBsb2NhdGlvbnMgb2YgdGhlIGJpa2UgcmVudGFsIHN0YXRpb25zCgpIZXJlIGlzIHRoZSBjb2RlIHRvIHJlYWQgaW4gdGhlIGRhdGEuIFdlIGRvIHRoaXMgYSBsaXR0bGUgZGlmZmVyZW50bHkgdGhhbiB1c3VhbHksIHdoaWNoIGlzIHdoeSBpdCBpcyBpbmNsdWRlZCBoZXJlIHJhdGhlciB0aGFuIGF0IHRoZSB0b3Agb2YgdGhpcyBmaWxlLiBUbyBhdm9pZCByZXBlYXRlZGx5IHJlLXJlYWRpbmcgdGhlIGZpbGVzLCBzdGFydCB0aGUgZGF0YSBpbXBvcnQgY2h1bmsgd2l0aCBge3IgY2FjaGUgPSBUUlVFfWAgcmF0aGVyIHRoYW4gdGhlIHVzdWFsIGB7cn1gLiBUaGlzIGNvZGUgcmVhZHMgaW4gdGhlIGxhcmdlIGRhdGFzZXQgcmlnaHQgYXdheS4KCmBgYHtyIGNhY2hlPVRSVUV9CmRhdGFfc2l0ZSA8LSAKICAiaHR0cHM6Ly93d3cubWFjYWxlc3Rlci5lZHUvfmRzaHVtYW4xL2RhdGEvMTEyLzIwMTQtUTQtVHJpcHMtSGlzdG9yeS1EYXRhLnJkcyIgClRyaXBzIDwtIHJlYWRSRFMoZ3pjb24odXJsKGRhdGFfc2l0ZSkpKQpTdGF0aW9uczwtcmVhZF9jc3YoImh0dHA6Ly93d3cubWFjYWxlc3Rlci5lZHUvfmRzaHVtYW4xL2RhdGEvMTEyL0RDLVN0YXRpb25zLmNzdiIpCmBgYAoKICA5LiBVc2UgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdmFyaWFibGVzIGluIGBTdGF0aW9uc2AgdG8gbWFrZSBhIHZpc3VhbGl6YXRpb24gb2YgdGhlIHRvdGFsIG51bWJlciBvZiBkZXBhcnR1cmVzIGZyb20gZWFjaCBzdGF0aW9uIGluIHRoZSBgVHJpcHNgIGRhdGEuIFVzZSBlaXRoZXIgY29sb3Igb3Igc2l6ZSB0byBzaG93IHRoZSB2YXJpYXRpb24gaW4gbnVtYmVyIG9mIGRlcGFydHVyZXMuIFRoaXMgdGltZSwgcGxvdCB0aGUgcG9pbnRzIG9uIHRvcCBvZiBhIG1hcC4gVXNlIGFueSBvZiB0aGUgbWFwcGluZyB0b29scyB5b3UnZCBsaWtlLgogIApgYGB7cn0KVHJpcHMyIDwtIFRyaXBzICU+JSAKICBncm91cF9ieShzc3RhdGlvbikgJT4lIAogIHN1bW1hcml6ZShuX2RlcGFydHVyZXMgPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2Mobl9kZXBhcnR1cmVzKSkgJT4lIAogIGxlZnRfam9pbihTdGF0aW9ucywgCiAgICAgICAgICAgIGJ5ID0gYygic3N0YXRpb24iPSJuYW1lIikpIAoKCnBhbDIgPC0gY29sb3JCaW4ocGFsZXR0ZT0iWWxHbkJ1IiwKICAgICAgICAgICAgICAgICBkb21haW4gPSBUcmlwczIkbl9kZXBhcnR1cmVzLAogICAgICAgICAgICAgICAgIGJpbnM9NikKCmxlYWZsZXQoVHJpcHMyKSAlPiUgCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLlRvbmVyKSAlPiUgCiAgYWRkQ2lyY2xlcyhsYWJlbCA9IH5zc3RhdGlvbiwgCiAgICAgICAgICAgICBjb2xvciA9IH5wYWwyKG5fZGVwYXJ0dXJlcyksIAogICAgICAgICAgICAgb3BhY2l0eSA9IDEpICU+JSAKICBhZGRMZWdlbmQoInRvcGxlZnQiLCB2YWx1ZXMgPSB+bl9kZXBhcnR1cmVzLCBwYWw9cGFsMiwgYmlucz02LCB0aXRsZT0iTnVtYmVyIG9mIFN0YXRpb24gRGVwYXJ0dXJlcyIpCgpgYGAKICAKICAxMC4gT25seSAxNC40JSBvZiB0aGUgdHJpcHMgaW4gb3VyIGRhdGEgYXJlIGNhcnJpZWQgb3V0IGJ5IGNhc3VhbCB1c2Vycy4gQ3JlYXRlIGEgcGxvdCB0aGF0IHNob3dzIHdoaWNoIGFyZWEocykgaGF2ZSBzdGF0aW9ucyB3aXRoIGEgbXVjaCBoaWdoZXIgcGVyY2VudGFnZSBvZiBkZXBhcnR1cmVzIGJ5IGNhc3VhbCB1c2Vycy4gV2hhdCBwYXR0ZXJucyBkbyB5b3Ugbm90aWNlPyBBbHNvIHBsb3QgdGhpcyBvbiB0b3Agb2YgYSBtYXAuIEkgdGhpbmsgaXQgd2lsbCBiZSBtb3JlIGNsZWFyIHdoYXQgdGhlIHBhdHRlcm5zIGFyZS4KICAKYGBge3J9CkNhc3VhbFRyaXBzIDwtIFRyaXBzICU+JSAKICBncm91cF9ieShzc3RhdGlvbikgJT4lCiAgbXV0YXRlKGNhc3VhbCA9IGNsaWVudCA9PSAiQ2FzdWFsIiwgCiAgICAgICAgIHJlZ2lzdGVyZWQgPSBjbGllbnQgPT0gIlJlZ2lzdGVyZWQiKSAlPiUgCiAgZ3JvdXBfYnkoc3N0YXRpb24pICU+JSAKICBzdW1tYXJpemUocHJvcF9jYXN1YWwgPSBzdW0oY2FzdWFsKS8oc3VtKHJlZ2lzdGVyZWQpICsgc3VtKGNhc3VhbCkpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHByb3BfY2FzdWFsKSkgJT4lIAogIGxlZnRfam9pbihTdGF0aW9ucywgCiAgICAgICAgICAgIGJ5ID0gYygic3N0YXRpb24iPSJuYW1lIikpIAoKcGFsMyA8LSBjb2xvckJpbihwYWxldHRlID0gIllsR25CdSIsIAogICAgICAgICAgICAgICAgIGRvbWFpbiA9IENhc3VhbFRyaXBzJHByb3BfY2FzdWFsKQoKbGVhZmxldChDYXN1YWxUcmlwcykgJT4lIAogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lcikgJT4lIAogIGFkZENpcmNsZXMobGFiZWwgPSB+c3N0YXRpb24sIAogICAgICAgICAgICAgY29sb3IgPSB+cGFsMyhwcm9wX2Nhc3VhbCksIAogICAgICAgICAgICAgb3BhY2l0eSA9IDEpICU+JSAKICBhZGRMZWdlbmQoImJvdHRvbWxlZnQiLCBwYWwgPSBwYWwzLCB2YWx1ZXMgPSB+cHJvcF9jYXN1YWwsIGJpbnMgPSA2LCB0aXRsZSA9ICJQcm9wb3J0aW9uIG9mIENhc3VhbCBDbGllbnRzIikKYGBgCgoqKk9ic2VydmF0aW9uczoqKiBXaXRoIHRoZSBtYXAsIGl0J3MgY2xlYXJlciB0byBpZGVudGlmeSB3aGljaCBzdGF0aW9ucyBoYXZlIGhpZ2hlciBwcm9wb3J0aW9ucyBvZiBjYXVzYWwgY2xpZW50cyBhbmQgd2h5LiBJdCBhcHBlYXJzIHRoYXQgdGhlIHN0YXRpb25zIHdpdGggdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiBvZiBjYXN1YWwgY2xpZW50cyBhcmUgYXJvdW5kIGtleSB0b3VyaXN0IGF0dHJhY3Rpb25zIGluIEQuQy4gRm9yIGluc3RhbmNlLCB0aGVyZSBhcmUgc3RhdGlvbnMgYXJvdW5kIHRoZSBtYWluIE1hbGwsIHRoZSBXYXNoaW5ndG9uIE1vbnVtZW50LCB0aGUgTUxLIE1lbW9yaWFsLCB0aGUgTGluY29sbiBNZW1vcmlhbCwgYW5kIHRoZSBDYXBpdG9sIEJ1aWxkaW5nIGFsbCB3aXRoIG5vdGFibHkgaGlnaGVyIHByb3BvcnRpb25zIG9mIGNhc3VhbCBjbGllbnRzLiBUaGlzIG1ha2VzIHNlbnNlIGFzIHRvdXJpc3RzIHdpbGwgYmUgbW9yZSBsaWtlbHkgdG8gdmlzaXQgdGhlc2UgYXJlYXMgYW5kIGNob29zZSB0byBjb21tdXRlIHdpdGggYmlrZXMuICAKICAKIyMjIENPVklELTE5IGRhdGEKClRoZSBmb2xsb3dpbmcgZXhlcmNpc2VzIHdpbGwgdXNlIHRoZSBDT1ZJRC0xOSBkYXRhIGZyb20gdGhlIE5ZVC4KCiAgMTEuIENyZWF0ZSBhIG1hcCB0aGF0IGNvbG9ycyB0aGUgc3RhdGVzIGJ5IHRoZSBtb3N0IHJlY2VudCBjdW11bGF0aXZlIG51bWJlciBvZiBDT1ZJRC0xOSBjYXNlcyAocmVtZW1iZXIsIHRoZXNlIGRhdGEgcmVwb3J0IGN1bXVsYXRpdmUgbnVtYmVycyBzbyB5b3UgZG9uJ3QgbmVlZCB0byBjb21wdXRlIHRoYXQpLiBEZXNjcmliZSB3aGF0IHlvdSBzZWUuIFdoYXQgaXMgdGhlIHByb2JsZW0gd2l0aCB0aGlzIG1hcD8KCmBgYHtyfQpTdGF0ZXNNYXAgPC0gbWFwX2RhdGEoInN0YXRlIikKCmNvdmlkMTkgJT4lIAogIG11dGF0ZShyZWdpb24gPSB0b2xvd2VyKHN0YXRlKSkgJT4lIAogIGdyb3VwX2J5KHJlZ2lvbikgJT4lIAogIG11dGF0ZShjdW1fY2FzZXMgPSBtYXgoY2FzZXMpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fbWFwKG1hcCA9IFN0YXRlc01hcCwKICAgICAgICAgICBhZXMobWFwX2lkID0gcmVnaW9uLAogICAgICAgICAgICAgICBmaWxsID0gY3VtX2Nhc2VzKSkgKwogIGV4cGFuZF9saW1pdHMoeCA9IFN0YXRlc01hcCRsb25nLCB5ID0gU3RhdGVzTWFwJGxhdCkgKyAKICB0aGVtZV9tYXAoKSArIAogIGxhYnModGl0bGUgPSAiQ09WSUQtMTkgQ2FzZXMgYnkgU3RhdGUiLCAKICAgICAgIHggPSAiIiwgCiAgICAgICB5ID0gIiIsIAogICAgICAgZmlsbCA9ICJUb3RhbCBDdW11bGF0aXZlIENhc2VzIGJ5IFN0YXRlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipEaXNjdXNzaW9uOioqIFRoaXMgbWFwIHNob3dzIHRoZSBudW1iZXIgb2YgY3VtdWxhdGl2ZSBDT1ZJRC0xOSBjYXNlcyBieSBzdGF0ZS4gRnJvbSB0aGUgbWFwLCB3ZSBjYW4gc2VlIHRoYXQgQ2FsaWZvcm5pYSwgVGV4YXMsIE5ldyBZb3JrLCBGbG9yaWRhLCBhbmQgSWxsaW5vaXMgYXJlIGFtb25uZyB0aGUgc3RhdGVzIHdpdGggdGhlIG1vc3QgY2FzZXMgaW4gdGhlIFVTLiBUaGUgaXNzdWUgd2l0aCB0aGlzIG1hcCBpcyB0aGF0IGl0J3MgbG9va2luZyBhdCByYXcgY291bnRzIG9mIGNhc2VzIHBlciBzdGF0ZSwgcmF0aGVyIHRoYW4gYSBwZXIgY2FwaXRhIG1lYXN1cmUgKGEgcmF0ZSkuIE9ubHkgbG9va2luZyBhdCByYXcgZGF0YSB3aWxsIG1ha2UgaXQgc3VjaCB0aGF0IHRoZSBtb3N0IHBvcHVsb3VzIHN0YXRlcyB3aWxsIGFsd2F5cyBhcHBlYXIgdG8gYmUgdGhlIG1vc3QgaW4gbWFnbml0dWRlLiBJVCB3b3VsZG4ndCBiZSBmYWlyIHRvIGNvbXBhcmUgcmF3IGNvdW50cyBvZiBjb3ZpZCBjYXNlcyBiZXR3ZWVuIENhbGlmb3JuaWEgYW5kIFd5b21pbmcsIHRoZSBwb3B1bGF0aW9uIHNpemVzIGFyZSBkcmFzdGljYWxseSBkaWZmZXJlbnQuIEl0IHdvdWxkIGJlIG1vcmUgYXBwcm9wcmlhdGUgdG8gbG9vayBhdCBhIHN0YW5kYXJkaXplZCBtZWFzdXJlIG9yIHJhdGUgb2YgQ09WSUQgY2FzZXMgaW5zdGVhZC4gCgogIDEyLiBOb3cgYWRkIHRoZSBwb3B1bGF0aW9uIG9mIGVhY2ggc3RhdGUgdG8gdGhlIGRhdGFzZXQgYW5kIGNvbG9yIHRoZSBzdGF0ZXMgYnkgbW9zdCByZWNlbnQgY3VtdWxhdGl2ZSBjYXNlcy8xMCwwMDAgcGVvcGxlLiBTZWUgdGhlIGNvZGUgZm9yIGRvaW5nIHRoaXMgd2l0aCB0aGUgU3RhcmJ1Y2tzIGRhdGEuIFlvdSB3aWxsIG5lZWQgdG8gbWFrZSBzb21lIG1vZGlmaWNhdGlvbnMuIAogIApgYGB7cn0KY292aWQxOV93aXRoX3BvcF9lc3QgPC0gY292aWQxOSAlPiUKICBtdXRhdGUoc3RhdGUgPSB0b2xvd2VyKHN0YXRlKSkgJT4lIAogIGxlZnRfam9pbihjZW5zdXNfcG9wX2VzdF8yMDE4LCAKICAgICAgICAgICAgYnkgPSBjKCJzdGF0ZSIpKSAlPiUgCiAgZ3JvdXBfYnkoc3RhdGUpICU+JSAKICBtdXRhdGUoY3VtX2Nhc2VzID0gbWF4KGNhc2VzKSwgCiAgICAgICAgICAgIGNhc2VzX3Blcl8xMDAwMCA9IChjdW1fY2FzZXMvZXN0X3BvcF8yMDE4KSAqIDEwMDAwKQoKY292aWQxOV93aXRoX3BvcF9lc3QgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX21hcChtYXAgPSBTdGF0ZXNNYXAsCiAgICAgICAgICAgYWVzKG1hcF9pZCA9IHN0YXRlLAogICAgICAgICAgICAgICBmaWxsID0gY2FzZXNfcGVyXzEwMDAwKSkgKwogIGV4cGFuZF9saW1pdHMoeCA9IFN0YXRlc01hcCRsb25nLCB5ID0gU3RhdGVzTWFwJGxhdCkgKyAKICB0aGVtZV9tYXAoKSArIAogIGxhYnModGl0bGUgPSAiQ09WSUQtMTkgQ2FzZXMgcGVyIDEwLDAwMCBQZW9wbGUgYnkgU3RhdGUiLCAKICAgICAgIHggPSAiIiwgCiAgICAgICB5ID0gIiIsIAogICAgICAgZmlsbCA9ICJUb3RhbCBDdW11bGF0aXZlIENhc2VzIHBlciAxMDAwMCBQZW9wbGUgYnkgU3RhdGUiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKICAKICAxMy4gKipDSEFMTEVOR0UqKiBDaG9vc2UgNCBkYXRlcyBzcHJlYWQgb3ZlciB0aGUgdGltZSBwZXJpb2Qgb2YgdGhlIGRhdGEgYW5kIGNyZWF0ZSB0aGUgc2FtZSBtYXAgYXMgaW4gZXhlcmNpc2UgMTIgZm9yIGVhY2ggb2YgdGhlIGRhdGVzLiBEaXNwbGF5IHRoZSBmb3VyIGdyYXBocyB0b2dldGhlciB1c2luZyBmYWNldGluZy4gV2hhdCBkbyB5b3Ugbm90aWNlPwoKYGBge3J9CmNvdmlkXzRkYXlfc2FtcGxlIDwtIGNvdmlkMTkgJT4lIAogIG11dGF0ZShzdGF0ZSA9IHRvbG93ZXIoc3RhdGUpKSAlPiUgCiAgbGVmdF9qb2luKGNlbnN1c19wb3BfZXN0XzIwMTgsIAogICAgICAgICAgICBieSA9IGMoInN0YXRlIikpICU+JSAKICBncm91cF9ieShkYXRlLCBzdGF0ZSkgJT4lIAogIG11dGF0ZShjdW1fY2FzZXMgPSBtYXgoY2FzZXMpLCAKICAgICAgICAgICAgY2FzZXNfcGVyXzEwMDAwID0gKGN1bV9jYXNlcy9lc3RfcG9wXzIwMTgpICogMTAwMDApCgpjb3ZpZF80ZGF5X3NhbXBsZSAlPiUgCiAgZmlsdGVyKGRhdGUgPT0gIjIwMjEtMDEtMDEifAogICAgICAgICBkYXRlID09ICIyMDIwLTEwLTAxInwKICAgICAgICAgZGF0ZSA9PSAiMjAyMC0wNy0wMSJ8CiAgICAgICAgIGRhdGUgPT0gIjIwMjAtMDQtMDEiKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fbWFwKG1hcCA9IFN0YXRlc01hcCwKICAgICAgICAgICBhZXMobWFwX2lkID0gc3RhdGUsCiAgICAgICAgICAgICAgIGZpbGwgPSBjYXNlc19wZXJfMTAwMDApKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gU3RhdGVzTWFwJGxvbmcsIHkgPSBTdGF0ZXNNYXAkbGF0KSArIAogIHRoZW1lX21hcCgpICsKICBsYWJzKHRpdGxlID0gIkNPVklELTE5IENhc2VzIHBlciAxMCwwMDAgUGVvcGxlIGJ5IFN0YXRlIiwgCiAgICAgICB4ID0gIiIsIAogICAgICAgeSA9ICIiLCAKICAgICAgIGZpbGwgPSAiVG90YWwgQ3VtdWxhdGl2ZSBDYXNlcyBwZXIgMTAwMDAgUGVvcGxlIGJ5IFN0YXRlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSsKICBmYWNldF93cmFwKHZhcnMoZGF0ZSkpCiAgCmBgYAoKKipEaXNjdXNzaW9uOioqIFlvdSBjYW4gc2VlIGZyb20gdGhlIG1hcHMgdGhhdCBDb3ZpZCBjYXNlcyBwZXIgMTAsMDAwIGhhdmUgZHJhc3RpY2FsbHkgaW5jcmVhc2VkIHNpbmNlIHRoZSBiZWdpbm5pbmcgb2YgdGhlIHBhbmRlbWljLiBCZWNhdXNlIHRoZSBzY2FsZSBpcyBmaXhlZCBmb3IgZWFjaCBvZiB0aGUgbWFwcywgaXRzIGJvdW5kIHRvIHRoZSB1cHBlciBsaW1pdHMgb2YgdGhlIG1vc3QgcmVjZW50IGNhc2UgY291bnRzLCB3aGljaCBhcmUgbXVjaCBtdWNoIGhpZ2hlciB0aGFuIHRvIHN0YXJ0LiBUaGlzIGNhdXNlcyB0aGUgaW5pdGlhbCBtYXBzIGluIHRoZSBlYXJsaWVyIG1vbnRocyBvZiB0aGUgcGFuZGVtaWMgdG8gYXBwZWFyIGFzIGlmIHRoZSBjYXNlcyB3ZXJlIHZlcnkgbG93LCB0aG91Z2ggdGhpcyBpcyBvbmx5IGEgcmVzdWx0IG9mIHRoZSBoaWdoIGNhc2VzIG51bWJlcnMgdG9kYXkuIEFzIHN1Y2gsIHdlJ3JlIHVuYWJsZSB0byBzZWUgdGhlIGdyYWRhdGlvbiBpbiB0aGUgc2V2ZXJpdHkgb2YgY2FzZXMgYWNyb3NzIHN0YXRlcyB1bnRpbCBhYm91dCBPY3RvYmVyLCB3aGljaCBzdGlsbCBkb2Vzbid0IHByb3ZpZGUgYSB0b24gb2YgY2xhcml0eSBhcm91bmQgd2hpY2ggc3RhdGVzIGFyZSBkb2luZyB3b3JzZS9iZXR0ZXIgdGhhbiBvdGhlcnMuIFlvdSBjYW4gc2VlIHRocm91Z2hvdXQgdGhlc2UgbWFwcywgaG93ZXZlciwgdGhhdCB0aGUgUGFjaWZpYyBOb3J0aHdlc3QgaGFzIGNvbnNpc3RlbnRseSBsb3cgY2FzZXMgdGhyb3VnaG91dCB0aGlzIHRpbWUgcGVyaW9kIHNhbXBsZS4gSSdtIHN1cnByaXNlZCB0byBzZWUgaG93IGhpZ2ggdGhlIGNhc2VzIHBlciAxMCwwMDAgaGFzIGdvdHRlbiBmb3IgdGhlIERha290YXMsIGl0IGFwcGVhcnMgdGhleSBhcmUgYW1vbmcgdGhlIGhpZ2hlc3QgY2FzZSBjb3VudHMgaW4gdGhlIGNvdW50cnkuIAoKIyMgTWlubmVhcG9saXMgcG9saWNlIHN0b3BzCgpUaGVzZSBleGVyY2lzZXMgdXNlIHRoZSBkYXRhc2V0cyBgTXBsc1N0b3BzYCBhbmQgYE1wbHNEZW1vYCBmcm9tIHRoZSBgY2FyRGF0YWAgbGlicmFyeS4gU2VhcmNoIGZvciB0aGVtIGluIEhlbHAgdG8gZmluZCBvdXQgbW9yZSBpbmZvcm1hdGlvbi4KCiAgMTQuIFVzZSB0aGUgYE1wbHNTdG9wc2AgZGF0YXNldCB0byBmaW5kIG91dCBob3cgbWFueSBzdG9wcyB0aGVyZSB3ZXJlIGZvciBlYWNoIG5laWdoYm9yaG9vZCBhbmQgdGhlIHByb3BvcnRpb24gb2Ygc3RvcHMgdGhhdCB3ZXJlIGZvciBhIHN1c3BpY2lvdXMgdmVoaWNsZSBvciBwZXJzb24uIFNvcnQgdGhlIHJlc3VsdHMgZnJvbSBtb3N0IHRvIGxlYXN0IG51bWJlciBvZiBzdG9wcy4gU2F2ZSB0aGlzIGFzIGEgZGF0YXNldCBjYWxsZWQgYG1wbHNfc3VzcGljaW91c2AgYW5kIGRpc3BsYXkgdGhlIHRhYmxlLiAgCiAgCmBgYHtyfQptcGxzX3N1c3BpY2lvdXMgPC0gTXBsc1N0b3BzICU+JSAKICBtdXRhdGUoc3VzID0gcHJvYmxlbSA9PSAic3VzcGljaW91cyIpICU+JSAKICBncm91cF9ieShuZWlnaGJvcmhvb2QpICU+JSAKICBzdW1tYXJpemUobl9zdG9wcyA9IG4oKSwgCiAgICAgICAgICAgIHN1c19zdG9wcyA9IHN1bShzdXMpLCAKICAgICAgICAgICAgcHJvcF9zdXMgPSBzdXNfc3RvcHMvbl9zdG9wcykgJT4lIAogIGFycmFuZ2UoZGVzYyhuX3N0b3BzKSkKCmhlYWQobXBsc19zdXNwaWNpb3VzKQpgYGAKCiAgCiAgMTUuIFVzZSBhIGBsZWFmbGV0YCBtYXAgYW5kIHRoZSBgTXBsc1N0b3BzYCBkYXRhc2V0IHRvIGRpc3BsYXkgZWFjaCBvZiB0aGUgc3RvcHMgb24gYSBtYXAgYXMgYSBzbWFsbCBwb2ludC4gQ29sb3IgdGhlIHBvaW50cyBkaWZmZXJlbnRseSBkZXBlbmRpbmcgb24gd2hldGhlciB0aGV5IHdlcmUgZm9yIHN1c3BpY2lvdXMgdmVoaWNsZS9wZXJzb24gb3IgYSB0cmFmZmljIHN0b3AgKHRoZSBgcHJvYmxlbWAgdmFyaWFibGUpLiBISU5UUzogdXNlIGBhZGRDaXJjbGVNYXJrZXJzYCwgc2V0IGBzdHJva2UgPSBGQWxTRWAsIHVzZSBgY29sb3JGYWN0b3IoKWAgdG8gY3JlYXRlIGEgcGFsZXR0ZS4gIAoKYGBge3J9CnBhbDQgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9IGMoImJsdWUiLCAieWVsbG93IiksIAogICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IE1wbHNTdG9wcyRwcm9ibGVtKQoKbGVhZmxldChNcGxzU3RvcHMpICU+JSAKICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRTdGFtZW4uVG9uZXIpICU+JSAKICBhZGRDaXJjbGVzKGxuZyA9IH5sb25nLAogICAgICAgICAgICAgbGF0ID0gfmxhdCwKICAgICAgICAgICAgIHJhZGl1cyA9IDMsCiAgICAgICAgICAgICBjb2xvciA9IH5wYWw0KHByb2JsZW0pLAogICAgICAgICAgICAgc3Ryb2tlPUZBTFNFLCAKICAgICAgICAgICAgIGxhYmVsPSB+cHJvYmxlbSkgJT4lIAogICAgYWRkTGVnZW5kKCJ0b3BsZWZ0IiwgcGFsID0gcGFsNCwgdmFsdWVzID0gfnByb2JsZW0sIGJpbnM9MiwgdGl0bGUgPSAiU3RvcCBUeXBlIikKCmBgYAoKCiAgMTYuIFNhdmUgdGhlIGZvbGRlciBmcm9tIG1vb2RsZSBjYWxsZWQgTWlubmVhcG9saXNfTmVpZ2hib3Job29kcyBpbnRvIHlvdXIgcHJvamVjdC9yZXBvc2l0b3J5IGZvbGRlciBmb3IgdGhpcyBhc3NpZ25tZW50LiBNYWtlIHN1cmUgdGhlIGZvbGRlciBpcyBjYWxsZWQgTWlubmVhcG9saXNfTmVpZ2hib3Job29kcy4gVXNlIHRoZSBjb2RlIGJlbG93IHRvIHJlYWQgaW4gdGhlIGRhdGEgYW5kIG1ha2Ugc3VyZSB0byAqKmRlbGV0ZSB0aGUgYGV2YWw9RkFMU0VgKiouIEFsdGhvdWdoIGl0IGxvb2tzIGxpa2UgaXQgb25seSBsaW5rcyB0byB0aGUgLnNwaCBmaWxlLCB5b3UgbmVlZCB0aGUgZW50aXJlIGZvbGRlciBvZiBmaWxlcyB0byBjcmVhdGUgdGhlIGBtcGxzX25iaGRgIGRhdGEgc2V0LiBUaGVzZSBkYXRhIGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgdGhlIGdlb21ldHJpZXMgb2YgdGhlIE1pbm5lYXBvbGlzIG5laWdoYm9yaG9vZHMuIFVzaW5nIHRoZSBgbXBsc19uYmhkYCBkYXRhc2V0IGFzIHRoZSBiYXNlIGZpbGUsIGpvaW4gdGhlIGBtcGxzX3N1c3BpY2lvdXNgIGFuZCBgTXBsc0RlbW9gIGRhdGFzZXRzIHRvIGl0IGJ5IG5laWdoYm9yaG9vZCAoY2FyZWZ1bCwgdGhleSBhcmUgbmFtZWQgZGlmZmVyZW50IHRoaW5ncyBpbiB0aGUgZGlmZmVyZW50IGZpbGVzKS4gQ2FsbCB0aGlzIG5ldyBkYXRhc2V0IGBtcGxzX2FsbGAuCgpgYGB7cn0KbXBsc19uYmhkIDwtIHN0X3JlYWQoIk1pbm5lYXBvbGlzX05laWdoYm9yaG9vZHMvTWlubmVhcG9saXNfTmVpZ2hib3Job29kcy5zaHAiLCBxdWlldCA9IFRSVUUpCmBgYAoKYGBge3J9Cm1wbHNfc3VzcGljaW91c19mdWxsIDwtIG1wbHNfc3VzcGljaW91cyAlPiUgCiAgbGVmdF9qb2luKE1wbHNTdG9wcywKICAgICAgICAgICAgYnk9Im5laWdoYm9yaG9vZCIpCgptcGxzX25iaGRfY29weSA8LSBtcGxzX25iaGQgJT4lIAogIGxlZnRfam9pbihtcGxzX3N1c3BpY2lvdXNfZnVsbCwKICAgICAgICAgICAgYnk9YygiQkROQU1FIj0ibmVpZ2hib3Job29kIikpCgptcGxzX2FsbCA8LSBtcGxzX25iaGRfY29weSAlPiUgCiAgbGVmdF9qb2luKE1wbHNEZW1vLCAKICAgICAgICAgICAgYnk9YygiQkROQU1FIj0ibmVpZ2hib3Job29kIikpCmBgYAoKCiAgMTcuIFVzZSBgbGVhZmxldGAgdG8gY3JlYXRlIGEgbWFwIGZyb20gdGhlIGBtcGxzX2FsbGAgZGF0YSAgdGhhdCBjb2xvcnMgdGhlIG5laWdoYm9yaG9vZHMgYnkgYHByb3Bfc3VzcGljaW91c2AuIERpc3BsYXkgdGhlIG5laWdoYm9yaG9vZCBuYW1lIGFzIHlvdSBzY3JvbGwgb3ZlciBpdC4gRGVzY3JpYmUgd2hhdCB5b3Ugb2JzZXJ2ZSBpbiB0aGUgbWFwLgogIApgYGB7cn0KICBwYWw1IDwtIGNvbG9yQmluKHBhbGV0dGU9IkdyZWVucyIsCiAgICAgICAgICAgICAgICAgZG9tYWluID0gbXBsc19hbGwkcHJvcF9zdXMsCiAgICAgICAgICAgICAgICAgYmlucz02KQoKbGVhZmxldChtcGxzX2FsbCkgJT4lIAogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lcikgJT4lIAogIGFkZENpcmNsZXMobG5nID0gfmxvbmcsCiAgICAgICAgICAgICBsYXQgPSB+bGF0LAogICAgICAgICAgICAgcmFkaXVzID0gMywKICAgICAgICAgICAgIHN0cm9rZT1GQUxTRSwgCiAgICAgICAgICAgICBsYWJlbCA9IH5CRE5BTUUsCiAgICAgICAgICAgICBjb2xvciA9IH5wYWw1KHByb3Bfc3VzKSwKICAgICAgICAgICAgIG9wYWNpdHk9MSkgJT4lIAogIGFkZExlZ2VuZCgidG9wbGVmdCIsIHZhbHVlcyA9IH5wcm9wX3N1cywgcGFsPXBhbDUsIGJpbnM9NiwgdGl0bGU9IlByb3BvcnRpb24gb2YgU3VzcGljaW91cyBQb2xpY2UgU3RvcHMiKQogIApgYGAKCioqT2JzZXJ2YXRpb25zOioqIFRoZSBOZWlnaGJvcmhvb2RzIHRoYXQgYXBwZWFyIHRvIGhhdmUgdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiBvZiBwb2xpY2Ugc3RvcHMgZm9yIHBlb3BsZSBkZWVtZWQgc3VzcGljaW91cyBhcmUgY2x1c3RlcmVkIGFyb3VuZCB0aGUgZG93bnRvd24gYXJlYSBhbmQgdGhlIG5laWdoYm9yaG9vZHMgaW4gc291dGggTWlubmVhcG9saXMgdGhhdCBhcmUgYWxvbmcgMzVXIHN1Y2ggYXMgRWFzdCBQaGlsbGlwcywgQ2VudHJhbCwgUG93ZGVyaG9ybiBQYXJrLCBTZXdhcmQsIExvbmdmZWxsbG93LCBhbmQgQ29yY29yYW4uIFRoZXJlIGFyZSBmZXcgc3RvcHMgZm9yICdzdXNwaWNpb3VzJyBwZW9wbGUgaW4gTm9ydGhlYXN0IE1pbm5lYXBvbGlzIGFuZCBhcm91bmQgb3RoZXIgcGFydHMgbmVhciB0aGUgVSBvZiBNIGNhbXB1cy4gU2FtZSB3aXRoIGNoYWluIG9mIGxha2VzIGFyZWEuIEZyb20gcGVyc29uYWxseSBsaXZpbmcgaW4gdGhlIFR3aW4gQ2l0aWVzIGFsbCBteSBsaWZlLCBJIGtub3cgdGhlc2UgdHJlbmRzIHRvIHJvdWdobHkgZXF1YXRlIHRvIHRoZSBhZmZsdWVuY2Ugb2YgdGhlc2UgbmVpZ2hib3Job29kcy4gVGhlIG5laWdoYm9yaG9vZHMgd2l0aCB3ZWFsdGhpZXIgcGVvcGxlIHRlbmQgdG8gaGF2ZSBsb3dlciBwcm9wb3J0aW9ucyBvZiBzdG9wcyBmb3IgcGVvcGxlIHdobyBhcmUgJ3N1c3BpY2lvdXMuJyAKICAKICAxOC4gVXNlIGBsZWFmbGV0YCB0byBjcmVhdGUgYSBtYXAgb2YgeW91ciBvd24gY2hvb3NpbmcuIENvbWUgdXAgd2l0aCBhIHF1ZXN0aW9uIHlvdSB3YW50IHRvIHRyeSB0byBhbnN3ZXIgYW5kIHVzZSB0aGUgbWFwIHRvIGhlbHAgYW5zd2VyIHRoYXQgcXVlc3Rpb24uIERlc2NyaWJlIHdoYXQgeW91ciBtYXAgc2hvd3MuIAoKKipRdWVzdGlvbjoqKiBBcmUgcG9saWNlIHN0b3BzIGluIGNlcnRhaW4gbmVpZ2hib3Job29kcyBtb3JlIGxpa2VseSB0byByZXN1bHQgaW4gYSB2ZWhpY2xlIHNlYXJjaCB0aGFuIG90aGVyIG5laWdoYm9yaG9vZHM/IAoKYGBge3J9ClZlaGljbGVTZWFyY2hlZCA8LSBNcGxzU3RvcHMgJT4lCiAgZmlsdGVyKCFpcy5uYSh2ZWhpY2xlU2VhcmNoKSkgJT4lIAogIG11dGF0ZShzZWFyY2hfeWVzID0gdmVoaWNsZVNlYXJjaCA9PSAiWUVTIikgJT4lIAogIGdyb3VwX2J5KG5laWdoYm9yaG9vZCkgJT4lIAogIHN1bW1hcml6ZShuX3N0b3BzID0gbigpLCAKICAgICAgICAgICAgdl9zZWFyY2hfeWVzID0gc3VtKHNlYXJjaF95ZXMgPT0gVFJVRSksIAogICAgICAgICAgICBwcm9wX3NlYXJjaF95ZXMgPSB2X3NlYXJjaF95ZXMvbl9zdG9wcykgJT4lIAogIGFycmFuZ2UoZGVzYyhwcm9wX3NlYXJjaF95ZXMpKQoKVmVoaWNsZVNlYXJjaGVkX2Z1bGwgPC0gVmVoaWNsZVNlYXJjaGVkICU+JSAKICBsZWZ0X2pvaW4oTXBsc1N0b3BzLAogICAgICAgICAgICBieT0ibmVpZ2hib3Job29kIikKClZlaGljbGVTZWFyY2hlZF9uYmhkIDwtIG1wbHNfbmJoZCAlPiUgCiAgbGVmdF9qb2luKFZlaGljbGVTZWFyY2hlZF9mdWxsLAogICAgICAgICAgICBieT1jKCJCRE5BTUUiPSJuZWlnaGJvcmhvb2QiKSkKClZlaGljbGVTZWFyY2hlZEFsbCA8LSBWZWhpY2xlU2VhcmNoZWRfbmJoZCAlPiUgCiAgbGVmdF9qb2luKE1wbHNEZW1vLCAKICAgICAgICAgICAgYnk9YygiQkROQU1FIj0ibmVpZ2hib3Job29kIikpCgpwYWw2IDwtIGNvbG9yQmluKHBhbGV0dGUgPSAiR3JlZW5zIiwgCiAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gVmVoaWNsZVNlYXJjaGVkQWxsJHByb3Bfc2VhcmNoX3llcywgCiAgICAgICAgICAgICAgICAgICAgYmlucyA9IDYpCgpsZWFmbGV0KFZlaGljbGVTZWFyY2hlZEFsbCkgJT4lIAogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lcikgJT4lIAogIGFkZENpcmNsZXMobG5nID0gfmxvbmcsCiAgICAgICAgICAgICBsYXQgPSB+bGF0LAogICAgICAgICAgICAgcmFkaXVzID0gMywKICAgICAgICAgICAgIHN0cm9rZT1GQUxTRSwgCiAgICAgICAgICAgICBsYWJlbCA9IH5CRE5BTUUsCiAgICAgICAgICAgICBjb2xvciA9IH5wYWw2KHByb3Bfc2VhcmNoX3llcyksCiAgICAgICAgICAgICBvcGFjaXR5PTEpICU+JSAKICBhZGRMZWdlbmQoInRvcHJpZ2h0IiwgdmFsdWVzID0gfnByb3Bfc2VhcmNoX3llcywgcGFsPXBhbDYsIGJpbnM9MSwgdGl0bGU9IlByb3BvcnRpb24gb2YgU3RvcHMgdGhhdCBMZWQgdG8gYSBWZWhpY2xlIFNlYXJjaCIpCiAgCmBgYAoKKipEaXNjdXNzaW9uOioqIE15IG1hcCBpbGx1c3RyYXRlcyB0aGUgcHJvcG9ydGlvbiBvZiBzdG9wcyBieSB0aGUgcG9saWNlIHRoYXQgcmVzdWx0IGluIGEgdmVoaWNsZSBzZWFyY2gsIGNhdGVnb3JpemVkIGJ5IG5laWdoYm9yaG9vZCBpbiBNaW5uZWFwb2xpcy4gQXJlYXMgb24gdGhlIG1hcCB0aGF0IGFyZSBkYXJrZXIgaW5kaWNhdGUgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiB0b3RhbCBzdG9wcyB0aGF0IHJlc3VsdCBpbiBhIHZlaGljbGUgc2VhcmNoLiBGcm9tIHRoZSBtYXAsIHdlIGNhbiBzZWUgYSBjbGVhciBzcGF0aWFsIHBhdHRlcm4uIFZlaGljbGUgc2VhcmNoZXMgaGFwcGVuIGF0IGEgaGlnaGVyIHJhdGUgaW4gTm9ydGggTWlubmVhcG9saXMgYW5kIHRoZSBQaGlsbGlwcyBOZWlnaGJvcmhvb2QgdGhhbiB0aGUgcmVzdCBvZiBNaW5uZWFwb2xpcy4gV2l0aG91dCBtb3JlIGNvbnRleHQgYW5kIGRhdGEgYW5hbHlzaXMsIGl0IG1pZ2h0IGJlIGVycm9uZW91cyBvZiBtZSB0byBtYWtlIGFueSBjYXVzYWwgaW5mZXJlbmNlcy4gSSBkbyBzdXNwZWN0LCBob3dldmVyLCB0aGF0IHRoaXMgaXMgaW5kaWNhdGl2ZSBvZiBhIHJhY2lhbCBpbWJhbGFuY2UgaW4gdGhlIHByb3BvcnRpb24gb2YgdmVoaWNsZSBzZWFyY2hlcyB0aGF0IG9jY3VyIGFzIE5vcnRoIE1pbm5lYXBvbGlzIGFuZCB0aGUgUGhpbGxpcHMgbmVpZ2hib3Job29kIGhhdmUgYSBoaWdoZXIgcGVyY2VudGFnZSBvZiBQT0MgbGl2aW5nIGluIHRoZXNlIGFyZWFzLiBXZSBjYW4gZGVmaW5pdGVseSBzZWUgdGhhdCB0aGVyZSBpcyBkaXNwYXJpdHkgaW4gdGVybXMgb2YgdGhlIHJhdGUgYXQgd2hpY2ggdmVoaWNsZSBzZWFyY2hlcyBvY2N1ciBhY3Jvc3MgbmVpZ2hib3Job29kcy4gSSB3b3VsZCBiZSBjdXJpb3VzIHRvIHNlZSBob3cgZGlmZmVyZW50IHRoZSBtYXAgbWlnaHQgYmUgYW5kIGhvdyBkaWZmZXJlbnQgdGhlIHJhdGVzIG1pZ2h0IGJlIGlmIEkgaGFkIGNhbGN1bGF0ZWQgZm9yIGFueSBzZWFyY2gsIHdoZXRoZXIgb2YgYSBwZXJzb24gb3Igb2YgYSB2ZWhpY2xlLiAKICAKIyMgR2l0SHViIGxpbmsKCiAgMTkuIEJlbG93LCBwcm92aWRlIGEgbGluayB0byB5b3VyIEdpdEh1YiBwYWdlIHdpdGggdGhpcyBzZXQgb2YgV2Vla2x5IEV4ZXJjaXNlcy4gU3BlY2lmaWNhbGx5LCBpZiB0aGUgbmFtZSBvZiB0aGUgZmlsZSBpcyAwNF9leGVyY2lzZXMuUm1kLCBwcm92aWRlIGEgbGluayB0byB0aGUgMDRfZXhlcmNpc2VzLm1kIGZpbGUsIHdoaWNoIGlzIHRoZSBvbmUgdGhhdCB3aWxsIGJlIG1vc3QgcmVhZGFibGUgb24gR2l0SHViLgoKKipMaW5rOioqIGh0dHBzOi8vZ2l0aHViLmNvbS9ncmV5bm9sZHMxMTIxL1JleW5vbGRzX1dlZWtfNF9IVy9ibG9iL21haW4vMDRfZXhlcmNpc2VzLm1kCgoqKkRJRCBZT1UgUkVNRU1CRVIgVE8gVU5DT01NRU5UIFRIRSBPUFRJT05TIEFUIFRIRSBUT1A/KioK